@@ -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; } | |||||
} | |||||
} |
@@ -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<DiscoveryOptions> _options; | |||||
public DiscoveryOptionsExtension(Action<DiscoveryOptions> option) | |||||
{ | |||||
_options = option; | |||||
} | |||||
public void AddServices(IServiceCollection services) | |||||
{ | |||||
var discoveryOptions = new DiscoveryOptions(); | |||||
_options?.Invoke(discoveryOptions); | |||||
services.AddSingleton(discoveryOptions); | |||||
services.AddSingleton<IDiscoveryProviderFactory, DiscoveryProviderFactory>(); | |||||
services.AddSingleton<IProcessingServer, ConsulProcessingNodeServer>(); | |||||
services.AddSingleton(x => | |||||
{ | |||||
var configOptions = x.GetService<DiscoveryOptions>(); | |||||
var factory = x.GetService<IDiscoveryProviderFactory>(); | |||||
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<DiscoveryOptions> options) | |||||
{ | |||||
if (options == null) | |||||
{ | |||||
throw new ArgumentNullException(nameof(options)); | |||||
} | |||||
capOptions.RegisterExtension(new DiscoveryOptionsExtension(options)); | |||||
return capOptions; | |||||
} | |||||
} | |||||
} |
@@ -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); | |||||
} | |||||
} | |||||
} |
@@ -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); | |||||
} | |||||
} |
@@ -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<ConsulNodeDiscoveryProvider> _logger; | |||||
private readonly DiscoveryOptions _options; | |||||
private ConsulClient _consul; | |||||
public ConsulNodeDiscoveryProvider(ILoggerFactory logger, DiscoveryOptions options) | |||||
{ | |||||
_logger = logger.CreateLogger<ConsulNodeDiscoveryProvider>(); | |||||
_options = options; | |||||
InitClient(); | |||||
} | |||||
public void Dispose() | |||||
{ | |||||
_consul.Dispose(); | |||||
} | |||||
public async Task<IList<Node>> GetNodes() | |||||
{ | |||||
try | |||||
{ | |||||
var nodes = new List<Node>(); | |||||
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}"); | |||||
}); | |||||
} | |||||
} | |||||
} |
@@ -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<IList<Node>> GetNodes(); | |||||
Task RegisterNode(); | |||||
} | |||||
} |
@@ -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() | |||||
{ | |||||
} | |||||
} | |||||
} |
@@ -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; } | |||||
} | |||||
} |