.NET Core Consul服务注册一文中讲述了安装Consul,注册服务。
本文讲述在.NET Core项目中的Consul服务发现。和注册一样,为了复用还是新建一个类库。
首先我们还是启动3个AspNetCore服务,注册到Consul,如何注册可以看.NET Core Consul服务注册一文中的介绍。
启动好以后在Consul的监视界面localhost:8500中可以看到3个服务了,如图所示
接下来开始在ConsulServiceDiscovery类库项目中编写代码。
服务提供IServiceProvider接口和ConsulServiceProvider类代码
- namespace ConsulServiceDiscovery
- {
- public interface IServiceProvider
- {
- Task<IList<string>> GetServicesAsync(string serviceName);
- }
- }
- namespace ConsulServiceDiscovery
- {
- public class ConsulServiceProvider : IServiceProvider
- {
- private readonly ConsulClient _consulClient;
- public ConsulServiceProvider(Uri uri) {
- _consulClient = new ConsulClient(consulConfig =>
- {
- consulConfig.Address = uri;
- });
- }
- public async Task<IList<string>> GetServicesAsync(string serviceName)
- {
- // Health 当前consul里已注册的服务,健康检查的信息也拿过来
- var queryResult = await _consulClient.Health.Service(serviceName, string.Empty, true);
- var result = new List<string>();
- foreach (var item in queryResult.Response) {
- result.Add($"{item.Service.Address}:{item.Service.Port}");
- }
- return result;
- }
- }
- }
服务创建IServiceBuilder接口和ServiceBuilder类代码
- namespace ConsulServiceDiscovery.Builder
- {
- public interface IServiceBuilder
- {
- /// <summary>
- /// 服务提供者
- /// </summary>
- IServiceProvider ServiceProvider { get; set; }
- /// <summary>
- /// 服务名称
- /// </summary>
- string ServiceName { get; set; }
- /// <summary>
- /// Uri方案
- /// </summary>
- string UriScheme { get; set; }
- /// <summary>
- /// 使用哪种策略
- /// </summary>
- ILoadBalancer LoadBalancer { get; set; }
- Task<Uri> BuildAsync(string path);
- }
- }
- namespace ConsulServiceDiscovery.Builder
- {
- public class ServiceBuilder : IServiceBuilder
- {
- public IServiceProvider ServiceProvider { get; set; }
- public string ServiceName { get; set; }
- public string UriScheme { get; set; }
- public ILoadBalancer LoadBalancer { get; set; }
- public ServiceBuilder(IServiceProvider serviceProvider) {
- ServiceProvider = serviceProvider;
- }
-
- public async Task<Uri> BuildAsync(string path)
- {
- var serviceList = await ServiceProvider.GetServicesAsync(ServiceName);
- var service = LoadBalancer.Resolve(serviceList);
- var baseUri = new Uri($"{UriScheme}://{service}");
- var uri = new Uri(baseUri, path);
- return uri;
- }
- }
- }
负载方式,这里列举随机和轮询两种负载方式,ILoadBalancer接口,RandomLoadBalancer实现类,RoundRobinLoadBalancer实现类,TypeLoadBalancer静态类代码
- namespace ConsulServiceDiscovery.LoadBalancer
- {
- public interface ILoadBalancer
- {
- string Resolve(IList<string> services);
- }
- }
- namespace ConsulServiceDiscovery.LoadBalancer
- {
- public class RandomLoadBalancer : ILoadBalancer
- {
- private readonly Random _random = new Random();
- public string Resolve(IList<string> services)
- {
- var index = _random.Next(services.Count);
- return services[index];
- }
- }
- }
- namespace ConsulServiceDiscovery.LoadBalancer
- {
- public class RoundRobinLoadBalancer : ILoadBalancer
- {
- private readonly object _lock = new object();
- private int _index = 0;
- public string Resolve(IList<string> services)
- {
- lock (_lock) {
- if (_index >= services.Count) {
- _index = 0;
- }
- return services[_index++];
- }
- }
- }
- }
- namespace ConsulServiceDiscovery.LoadBalancer
- {
- public static class TypeLoadBalancer
- {
- // 单例
- public static ILoadBalancer RandomLoad = new RandomLoadBalancer();
- public static ILoadBalancer RoundRobinLoad = new RoundRobinLoadBalancer();
- }
- }
扩展方法创建ServiceBuilder
- namespace ConsulServiceDiscovery
- {
- public static class ServiceProviderExtension
- {
- public static IServiceBuilder CreateServiceBuilder(this IServiceProvider serviceProvider, Action<IServiceBuilder> config) {
- var builder = new ServiceBuilder(serviceProvider);
- config(builder);
- return builder;
- }
- }
- }
这样,服务发现的代码就完成了。
下面是调用测试代码,创建一个控制台项目,在Program.cs里使用轮询的方式代码如下
- namespace ConsulTest
- {
- class Program
- {
- static void Main(string[] args)
- {
- var serviceProvider = new ConsulServiceProvider(new Uri("http://localhost:8500"));
- var aspNetCoreService = serviceProvider.CreateServiceBuilder(builder => {
- builder.ServiceName = "AspNetCore";
- builder.LoadBalancer = TypeLoadBalancer.RoundRobinLoad;//可选RandomLoad
- builder.UriScheme = Uri.UriSchemeHttp;
- });
- var httpClient = new HttpClient();
- for (int i = 0; i < 100; i++) {
- Console.WriteLine($"----------第{i}次请求-----------");
- try
- {
- var uri = aspNetCoreService.BuildAsync("/Health").Result;
- Console.WriteLine($"{DateTime.Now} - 正在调用:{uri}");
- var content = httpClient.GetStringAsync(uri).Result;
- Console.WriteLine($"返回结果:{content}");
- }
- catch (Exception ex) {
- Console.WriteLine(ex.ToString());
- }
- Task.Delay(500).Wait();
- }
- Console.ReadLine();
- }
- }
- }
执行效果
可以看到,轮询方式会轮流请求1234,1235,1236端口的三个服务地址。