Net Core Consul

     Consul 是HashiCorp公司推出的开源工具,用于实现分布式系统的服务发现与配置,内置了服务注册发现框架,分布式一致性实现,健康检查,负载均衡,服务治理,多数据中心方案等。Consul用GoLang语言实现,因此具有天然可移植性(支持Linux,Window和Mac OS),安装包仅包含一个可执行文件(consul.exe),方便部署,与Docker等轻量级容器可无缝链接。

     要想利用Consul提供的服务实现服务的注册与实现,我们需要建立Consul Cluster,在Consul方案中,每个提供服务的节点上都要部署和运行Consul的Client Agent,所有运行Consul Agent节点的集合构成Consul Cluster。Consul Agent有两种运行模式:Server和Client。这里的Server和CLient只是Consul集群层面的区分,与搭建在Cluster之上的应用服务无关,以Server模式运行的Consul Agent节点用于维护Consul集群的状态,官方建议每个Consul Cluster至少有3个或以上的运行在Server Mode的Agent,Client节点不限。
    Consul支持多数据中心,每个数据中心的Consul Cluster都会运行于Server模式下的Agent节点中选出一个Leader节点,这个选举过程通过Consul实现Raft协议保证,多个Server节点上的Consul数据信息是强一致的,处于Client Mode的Consul Agent节点比较简单,无状态,仅仅负责将请求转发给Server Agent。

  Consul官网下载地址 Consul  解压文件,通过cmd指定解压文件路径

  本示例通过dev方式启动  如图所示:

服务注册到consul可以通过Http API (8500端口),Consul client及Consul配置文件的方式。

1、下面介绍通过Consul Client API方式将service注册到Consul:

1)、appsettings.json consul节点配置信息

1  "Consul": {
2     "Address": "http://127.0.0.1:8500",
3     "ServiceName": "ServiceA",
4     "ServiceIP": "localhost",
5     "ServicePort": "5000",
6     "HealthCheck": "/HealthCheck"
7   }
appsettings.json

2)、Nuget package下载Consul(1.6.10.8)

 3)、Program配置Consul

1 builder.Services.AddSingleton<IConsulClient, ConsulClient>(p 
2     => new ConsulClient(config => 
3     {
4         config.Address = new Uri(configuration["Consul:Address"]);
5     }));
Configure Service
1 #region Consul
2 app.UseConsul(configuration, app.Lifetime);
3 #endregion
WebApplication configure
 1  public static class CousulExtension
 2     {
 3         public static IApplicationBuilder UseConsul(this IApplicationBuilder app, IConfiguration configuration, IHostApplicationLifetime lifetime)
 4         {
 5             var client = new ConsulClient(option => 
 6             {
 7                 option.Address = new Uri(configuration["Consul:Address"]);
 8             });
 9 
10             var registration = new AgentServiceRegistration
11             {
12                 ID = Guid.NewGuid().ToString(),
13                 //Name = configuration["Consul:ServiceName"],
14                 //Address = configuration["Consul:ServiceIP"],
15                 //Port = Convert.ToInt32(configuration["Consul:ServicePort"]),
16                 //dotnet run --urls=https://localhost:5001 --ip=localhost --port=5001 --service=serviceA 
17                 Name = configuration["service"],
18                 Address = configuration["ip"],
19                 Port = Convert.ToInt32(configuration["port"]),
20                 Check = new AgentServiceCheck
21                 {
22                     DeregisterCriticalServiceAfter = TimeSpan.FromSeconds(5),
23                     Interval = TimeSpan.FromSeconds(10),
24                     //HTTP = $"https://{configuration["Consul:ServiceIP"]}:{configuration["Consul:ServicePort"]}{configuration["Consul:HealthCheck"]}",
25                     HTTP = $"https://{configuration["ip"]}:{configuration["port"]}{configuration["Consul:HealthCheck"]}",
26                     Timeout = TimeSpan.FromSeconds(5)
27                 }
28             };
29 
30             client.Agent.ServiceRegister(registration).GetAwaiter().GetResult();
31 
32             lifetime.ApplicationStopping.Register(() => 
33             {
34                 client.Agent.ServiceDeregister(registration.ID).GetAwaiter().GetResult();
35             });
36             return app;
37         }
38     }
consul extension

Notes: Register到Consul的service Id必须唯一,原因是通过Ocelot 集成Consul service需要指定ServiceName,如果Consul service需要负载均衡的话service要指定同一个ServiceName.

e.g.

 一个ServiceName有多个实例,从而可以做负载均衡。

4、创建一个NetCore API 项目,添加测试controller

 1  [HttpGet]
 2         public IActionResult Index([FromServices]IConfiguration configuration)
 3         {
 4             var result = new
 5             {
 6                 msg = $"This is service1, current date: {DateTime.Now:G}",
 7                 ip = configuration["ip"],
 8                 port = configuration["port"]
 9             };
10             return Ok(result);
11         }
12 
13         [Authorize]
14         [HttpGet("all")]
15         public IActionResult Get([FromServices] IConfiguration configuration)
16         {
17             return Ok(
18                 new
19                 {
20                     ID = 1,
21                     Name = "consul service",
22                     version = 1.0,
23                     serviceIP = configuration["ip"],
24                     servicePort = configuration["port"]
25                 });
26         }
service1 controller

添加健康检查API Controller

 1 [Route("[controller]")]
 2     [ApiController]
 3     public class HealthCheckController : Controller
 4     {
 5         [HttpGet]
 6         public IActionResult Index()
 7         {
 8             return Ok();
 9         }
10     }
HealthCheck controller

5、模拟服务集群创建多个服务实例分别注册到consul

dotnet run --urls=https://localhost:5001 --ip=localhost --port=5001 --service=serviceA

dotnet run --urls=https://localhost:5002 --ip=localhost --port=5002 --service=serviceA

dotnet run --urls=https://localhost:5003 --ip=localhost --port=5003 --service=serviceA

6、consul service

 

下面通过IConsulDiscoveryService测试是否可以正常从consul获取到服务

 1     public class ConsulDiscoveryService : IConsulDiscoveryService
 2     {
 3         private readonly IConfiguration _configuration;
 4         private IConsulClient _consulClient { get; set; }
 5         private readonly IHttpClientFactory _httpClientFactory;
 6         public ConsulDiscoveryService(IConfiguration configuration, IConsulClient consulClient, IHttpClientFactory httpClientFactory)
 7         {
 8             this._configuration = configuration;
 9             this._consulClient = new ConsulClient(options => 
10             {
11                 options.Address = new Uri(_configuration["Consul:Address"]);
12             });
13             this._httpClientFactory = httpClientFactory;
14         }
15 
16         public async Task<IEnumerable<HttpResponseMessage>> GetAsync()
17         {
18             var responses = new List<HttpResponseMessage>();
19             var serviceNames = new List<string>() { "Service1" };
20             var tasks = new List<Task>();
21             foreach (var serviceName in serviceNames)
22             {
23                 var task = Task.Run(async () =>
24                 {
25                     var options = new QueryOptions
26                     {
27                         Datacenter = "dc1",
28                         WaitTime = TimeSpan.FromMinutes(5)
29                     };
30                     var result = await _consulClient.Health.Service(serviceName, null, true, options);
31                     var services = result.Response;
32                     var serviceUrls = services.Select(s => $"https://{s.Service.Address}:{s.Service.Port}");
33                     foreach (var serviceUrl in serviceUrls)
34                     {
35                         using var httpClient = _httpClientFactory.CreateClient();
36                         var response = await httpClient.GetAsync($"{serviceUrl}/api/Service1/all");
37                         responses.Add(response);
38                     }
39                 });
40                 tasks.Add(task);
41             }
42             await Task.WhenAll(tasks);
43             return responses;
44         }
45     }
Consul Discovery Service

将ConsulDiscoveryService添加到Configure Service中

builder.Services.AddTransient<IConsulDiscoveryService, ConsulDiscoveryService>();
View Code

验证

[HttpGet("consulservice")]
        public async Task<IActionResult> GetConsulDiscoveryService([FromServices]IConsulDiscoveryService service)
        {
            var result = await service.GetAsync();
            return Ok(result);
        }
test

 2、通过配置文件的方式注册service,这里只创建了一个NetCore API作为demo

  1)、Add一个.json 文件

 1 {
 2   "services": [
 3     {
 4       "id": "consul_service01",
 5       "name": "service1",
 6       "tags": [
 7         "urlprefix-/ClientService01"
 8       ],
 9       "address": "localhost",
10       "port": 5000,
11       "checks": [
12         {
13           "name": "clientservice_check",
14           "http": "https://localhost:5000/HealthCheck",
15           "interval": "5s",
16           "timeout": "5s"
17         }
18       ]
19     }
20   ]
21 }
consul service configure

 2)、以熟悉的方式启动NetCore API,例: dotnet run

 3)、启动consul,依然以dev方式启动,这里在实际使用中通常创建consul cluster,官方建议3-5个consul server,consul client数量不限

consul agent -dev -node MicServices -config-dir service.consul.json

4)、consul中查看注册的service

 

 5)、通过API查看服务

 

 

 OK, 可以正常获取consul下的所有services。



posted @ 2023-02-07 20:39  云霄宇霁  阅读(45)  评论(0编辑  收藏  举报