diff --git a/src/DotNetCore.CAP.Dashboard/NodeDiscovery/CAP.DiscoveryOptions.cs b/src/DotNetCore.CAP.Dashboard/NodeDiscovery/CAP.DiscoveryOptions.cs new file mode 100644 index 0000000..6b720eb --- /dev/null +++ b/src/DotNetCore.CAP.Dashboard/NodeDiscovery/CAP.DiscoveryOptions.cs @@ -0,0 +1,38 @@ +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +namespace DotNetCore.CAP.Dashboard.NodeDiscovery +{ + public class DiscoveryOptions + { + public const string DefaultDiscoveryServerHost = "localhost"; + public const int DefaultDiscoveryServerPort = 8500; + + public const string DefaultCurrentNodeHostName = "localhost"; + public const int DefaultCurrentNodePort = 5000; + + public const string DefaultMatchPath = "/cap"; + + public DiscoveryOptions() + { + DiscoveryServerHostName = DefaultDiscoveryServerHost; + DiscoveryServerPort = DefaultDiscoveryServerPort; + + CurrentNodeHostName = DefaultCurrentNodeHostName; + CurrentNodePort = DefaultCurrentNodePort; + + MatchPath = DefaultMatchPath; + } + + public string DiscoveryServerHostName { get; set; } + public int DiscoveryServerPort { get; set; } + + public string CurrentNodeHostName { get; set; } + public int CurrentNodePort { get; set; } + + public string NodeId { get; set; } + public string NodeName { get; set; } + + public string MatchPath { get; set; } + } +} \ No newline at end of file diff --git a/src/DotNetCore.CAP.Dashboard/NodeDiscovery/CAP.DiscoveryOptionsExtensions.cs b/src/DotNetCore.CAP.Dashboard/NodeDiscovery/CAP.DiscoveryOptionsExtensions.cs new file mode 100644 index 0000000..400e91c --- /dev/null +++ b/src/DotNetCore.CAP.Dashboard/NodeDiscovery/CAP.DiscoveryOptionsExtensions.cs @@ -0,0 +1,56 @@ +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; +using DotNetCore.CAP; +using Microsoft.Extensions.DependencyInjection; + +namespace DotNetCore.CAP.Dashboard.NodeDiscovery +{ + internal sealed class DiscoveryOptionsExtension : ICapOptionsExtension + { + private readonly Action _options; + + public DiscoveryOptionsExtension(Action option) + { + _options = option; + } + + public void AddServices(IServiceCollection services) + { + var discoveryOptions = new DiscoveryOptions(); + + _options?.Invoke(discoveryOptions); + services.AddSingleton(discoveryOptions); + + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(x => + { + var configOptions = x.GetService(); + var factory = x.GetService(); + return factory.Create(configOptions); + }); + } + } + + public static class CapDiscoveryOptionsExtensions + { + public static CapOptions UseDiscovery(this CapOptions capOptions) + { + return capOptions.UseDiscovery(opt => { }); + } + + public static CapOptions UseDiscovery(this CapOptions capOptions, Action options) + { + if (options == null) + { + throw new ArgumentNullException(nameof(options)); + } + + capOptions.RegisterExtension(new DiscoveryOptionsExtension(options)); + + return capOptions; + } + } +} \ No newline at end of file diff --git a/src/DotNetCore.CAP.Dashboard/NodeDiscovery/IDiscoveryProviderFactory.Default.cs b/src/DotNetCore.CAP.Dashboard/NodeDiscovery/IDiscoveryProviderFactory.Default.cs new file mode 100644 index 0000000..38523b7 --- /dev/null +++ b/src/DotNetCore.CAP.Dashboard/NodeDiscovery/IDiscoveryProviderFactory.Default.cs @@ -0,0 +1,28 @@ +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; +using Microsoft.Extensions.Logging; + +namespace DotNetCore.CAP.Dashboard.NodeDiscovery +{ + internal class DiscoveryProviderFactory : IDiscoveryProviderFactory + { + private readonly ILoggerFactory _loggerFactory; + + public DiscoveryProviderFactory(ILoggerFactory loggerFactory) + { + _loggerFactory = loggerFactory; + } + + public INodeDiscoveryProvider Create(DiscoveryOptions options) + { + if (options == null) + { + throw new ArgumentNullException(nameof(options)); + } + + return new ConsulNodeDiscoveryProvider(_loggerFactory, options); + } + } +} \ No newline at end of file diff --git a/src/DotNetCore.CAP.Dashboard/NodeDiscovery/IDiscoveryProviderFactory.cs b/src/DotNetCore.CAP.Dashboard/NodeDiscovery/IDiscoveryProviderFactory.cs new file mode 100644 index 0000000..1839f22 --- /dev/null +++ b/src/DotNetCore.CAP.Dashboard/NodeDiscovery/IDiscoveryProviderFactory.cs @@ -0,0 +1,10 @@ +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +namespace DotNetCore.CAP.Dashboard.NodeDiscovery +{ + internal interface IDiscoveryProviderFactory + { + INodeDiscoveryProvider Create(DiscoveryOptions options); + } +} \ No newline at end of file diff --git a/src/DotNetCore.CAP.Dashboard/NodeDiscovery/INodeDiscoveryProvider.Consul.cs b/src/DotNetCore.CAP.Dashboard/NodeDiscovery/INodeDiscoveryProvider.Consul.cs new file mode 100644 index 0000000..6947b2d --- /dev/null +++ b/src/DotNetCore.CAP.Dashboard/NodeDiscovery/INodeDiscoveryProvider.Consul.cs @@ -0,0 +1,106 @@ +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Consul; +using Microsoft.Extensions.Logging; + +namespace DotNetCore.CAP.Dashboard.NodeDiscovery +{ + public class ConsulNodeDiscoveryProvider : INodeDiscoveryProvider, IDisposable + { + private readonly ILogger _logger; + private readonly DiscoveryOptions _options; + private ConsulClient _consul; + + public ConsulNodeDiscoveryProvider(ILoggerFactory logger, DiscoveryOptions options) + { + _logger = logger.CreateLogger(); + _options = options; + + InitClient(); + } + + public void Dispose() + { + _consul.Dispose(); + } + + public async Task> GetNodes() + { + try + { + var nodes = new List(); + var services = await _consul.Catalog.Services(); + foreach (var service in services.Response) + { + var serviceInfo = await _consul.Catalog.Service(service.Key); + var node = serviceInfo.Response.SkipWhile(x => !x.ServiceTags.Contains("CAP")) + .Select(info => new Node + { + Id = info.ServiceID, + Name = info.ServiceName, + Address = info.ServiceAddress, + Port = info.ServicePort, + Tags = string.Join(", ", info.ServiceTags) + }).ToList(); + + nodes.AddRange(node); + } + + CapCache.Global.AddOrUpdate("cap.nodes.count", nodes.Count, TimeSpan.FromSeconds(60), true); + + return nodes; + } + catch (Exception ex) + { + CapCache.Global.AddOrUpdate("cap.nodes.count", 0, TimeSpan.FromSeconds(20)); + + _logger.LogError( + $"Get consul nodes raised an exception. Exception:{ex.Message},{ex.InnerException.Message}"); + return null; + } + } + + public Task RegisterNode() + { + try + { + return _consul.Agent.ServiceRegister(new AgentServiceRegistration + { + ID = _options.NodeId, + Name = _options.NodeName, + Address = _options.CurrentNodeHostName, + Port = _options.CurrentNodePort, + Tags = new[] {"CAP", "Client", "Dashboard"}, + Check = new AgentServiceCheck + { + DeregisterCriticalServiceAfter = TimeSpan.FromSeconds(30), + Interval = TimeSpan.FromSeconds(10), + Status = HealthStatus.Passing, + HTTP = + $"http://{_options.CurrentNodeHostName}:{_options.CurrentNodePort}{_options.MatchPath}/health" + } + }); + } + catch (Exception ex) + { + _logger.LogError( + $"Get consul nodes raised an exception. Exception:{ex.Message},{ex.InnerException.Message}"); + return null; + } + } + + private void InitClient() + { + _consul = new ConsulClient(config => + { + config.WaitTime = TimeSpan.FromSeconds(5); + config.Address = new Uri($"http://{_options.DiscoveryServerHostName}:{_options.DiscoveryServerPort}"); + }); + } + } +} \ No newline at end of file diff --git a/src/DotNetCore.CAP.Dashboard/NodeDiscovery/INodeDiscoveryProvider.cs b/src/DotNetCore.CAP.Dashboard/NodeDiscovery/INodeDiscoveryProvider.cs new file mode 100644 index 0000000..a598087 --- /dev/null +++ b/src/DotNetCore.CAP.Dashboard/NodeDiscovery/INodeDiscoveryProvider.cs @@ -0,0 +1,15 @@ +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace DotNetCore.CAP.Dashboard.NodeDiscovery +{ + public interface INodeDiscoveryProvider + { + Task> GetNodes(); + + Task RegisterNode(); + } +} \ No newline at end of file diff --git a/src/DotNetCore.CAP.Dashboard/NodeDiscovery/IProcessingServer.Consul.cs b/src/DotNetCore.CAP.Dashboard/NodeDiscovery/IProcessingServer.Consul.cs new file mode 100644 index 0000000..c801698 --- /dev/null +++ b/src/DotNetCore.CAP.Dashboard/NodeDiscovery/IProcessingServer.Consul.cs @@ -0,0 +1,35 @@ +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +namespace DotNetCore.CAP.Dashboard.NodeDiscovery +{ + internal class ConsulProcessingNodeServer : IProcessingServer + { + private readonly DiscoveryOptions _dashboardOptions; + private readonly IDiscoveryProviderFactory _discoveryProviderFactory; + + public ConsulProcessingNodeServer( + DiscoveryOptions dashboardOptions, + IDiscoveryProviderFactory discoveryProviderFactory) + { + _dashboardOptions = dashboardOptions; + _discoveryProviderFactory = discoveryProviderFactory; + } + + public void Start() + { + var discoveryProvider = _discoveryProviderFactory.Create(_dashboardOptions); + + discoveryProvider.RegisterNode(); + } + + public void Pulse() + { + //ignore + } + + public void Dispose() + { + } + } +} \ No newline at end of file diff --git a/src/DotNetCore.CAP.Dashboard/NodeDiscovery/Node.cs b/src/DotNetCore.CAP.Dashboard/NodeDiscovery/Node.cs new file mode 100644 index 0000000..b016b50 --- /dev/null +++ b/src/DotNetCore.CAP.Dashboard/NodeDiscovery/Node.cs @@ -0,0 +1,18 @@ +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +namespace DotNetCore.CAP.Dashboard.NodeDiscovery +{ + public class Node + { + public string Id { get; set; } + + public string Name { get; set; } + + public string Address { get; set; } + + public int Port { get; set; } + + public string Tags { get; set; } + } +} \ No newline at end of file