Loading...

Asp.Net Core使用Ocelot结合Consul实现服务注册和发现_实用技巧

1.服务注册与发现(Service Discovery)

  • 服务注册:我们通过在每个服务实例写入注册代码,实例在启动的时候会先去注册中心(例如Consul、ZooKeeper、etcd、Eureka)注册一下,那么客户端通过注册中心可以知道每个服务实例的地址,端口号,健康状态等等信息,也可以通过注册中心删除服务实例。这里注册中心相当于是负责维护服务实例的管控中心。
  • 服务发现:服务实例在注册中心注册之后,客户端通过注册中心可以了解这些服务实例运行状况。

2.Consul

如果要实现服务注册与发现,需要一个注册中心,这里主要介绍是Consul。

Consul官网:https://www.consul.io/,它主要功能有:服务注册与发现、健康检查、Key/Value、多数据中心。

如果在Windows上部署Consul,在consul.exe目录下执行consul.exe agent -dev命令行即可。

3.Asp.Net Core向Consul注册服务实例

Asp.Net Core向Consul注册服务实例调用过程如下图所示:

Asp.Net Core向Consul注册服务实例需要在Gateway项目中引用Consul支持的NuGet软件包,安装命令如下:

Install-Package Ocelot.Provider.Consul

然后将以下内容添加到您的ConfigureServices方法中:

services.AddOcelot().AddConsul();

在Ocelot服务发现项目示例中,通过APIGateway项目GlobalConfiguration选项可以配置服务注册与发现,文件配置具体代码如下:

{    "Routes": [      {        "UseServiceDiscovery": true,        "DownstreamPathTemplate": "/{url}",        "DownstreamScheme": "http",        "ServiceName": "MyService",        "LoadBalancerOptions": {          "Type": "RoundRobin"        },        "UpstreamPathTemplate": "/{url}",        "UpstreamHttpMethod": [ "Get" ],        "ReRoutesCaseSensitive": false      }    ],    "GlobalConfiguration": {      //服务发现配置      "ServiceDiscoveryProvider": {        //注册中心Consul地址        "Host": "192.168.113.128",        //注册中心Consul端口号        "Port": 8500,        "Type": "Consul",        //以毫秒为单位,告诉Ocelot多久调用一次Consul来更改服务配置。        "PollingInterval": 100,        //如果你有在Consul上配置key/value,则在这里输入配置key。        "ConfigurationKey": "MyService_AB"      }    }  }

ServiceDiscoveryProvider选项说明:

  • Host:注册中心Consul地址。
  • Port:注册中心Consul端口号。
  • Type:注册中心类型。
  • PollingInterval:以毫秒为单位,告诉Ocelot多久调用一次Consul来更改服务配置。
  • ConfigurationKey:如果你有在Consul上配置key/value,则在这里输入配置key。

4.项目演示

4.1APIGateway项目

ConfigureServices添加Ocelot、Consul注入:

services.AddOcelot().AddConsul();

Configure添加使用Ocelot:

app.UseOcelot().Wait();

服务发现配置如Ocelot服务发现项目示例一样。

4.2Common项目

先安装Consul的NuGet软件包,安装命令如下:

Install-Package Consul

在该项目添加一个AppExtensions扩展类,用来对服务APIServiceA、APIServiceB项目在Consul注册实例,为了展示效果,具体代码稍作修改如下:

public static class AppExtensions  {      public static IServiceCollection AddConsulConfig(this IServiceCollection services, IConfiguration configuration)      {          services.AddSingleton<IConsulClient, ConsulClient>(p => new ConsulClient(consulConfig =>          {              var address = configuration.GetValue<string>("Consul:Host");              consulConfig.Address = new Uri(address);          }));          return services;      }      public static IApplicationBuilder UseConsul(this IApplicationBuilder app, string host = null, string port = null)      {          //获取consul客户端实例          var consulClient = app.ApplicationServices.GetRequiredService<IConsulClient>();          var logger = app.ApplicationServices.GetRequiredService<ILoggerFactory>().CreateLogger("AppExtensions");          var lifetime = app.ApplicationServices.GetRequiredService<IApplicationLifetime>();            if (!(app.Properties["server.Features"] is FeatureCollection features)) return app;            //var addresses = features.Get<IServerAddressesFeature>();          //var address = addresses.Addresses.FirstOrDefault();          //if (address == null)          //{          //    return app;          //}            var address = host + ":" + port;          if (string.IsNullOrWhiteSpace(host) || string.IsNullOrWhiteSpace(port))          {              Console.WriteLine($"host或者port为空!");              return app;          }            Console.WriteLine($"address={address}");          var uri = new Uri(address);          Console.WriteLine($"host={uri.Host},port={uri.Port}");            var registration = new AgentServiceRegistration()          {              ID = $"MyService-{uri.Port}",              Name = "MyService",              Address = $"{uri.Host}",              Port = uri.Port,              Check = new AgentServiceCheck()              {                  DeregisterCriticalServiceAfter = TimeSpan.FromSeconds(5),//服务启动多久后注册                  Interval = TimeSpan.FromSeconds(10),//健康检查时间间隔                  HTTP = $"{address}/HealthCheck",//健康检查地址                  Timeout = TimeSpan.FromSeconds(5)//超时时间              }          };          logger.LogInformation("Registering with Consul");          logger.LogInformation($"Consul RegistrationID:{registration.ID}");          //注销          consulClient.Agent.ServiceDeregister(registration.ID).ConfigureAwait(true);          //注册          consulClient.Agent.ServiceRegister(registration).ConfigureAwait(true);          //应用程序关闭时候          lifetime.ApplicationStopping.Register(() =>          {              //正在注销              logger.LogInformation("Unregistering from Consul");              consulClient.Agent.ServiceDeregister(registration.ID).ConfigureAwait(true);          });          //每个服务都需要提供一个用于健康检查的接口,该接口不具备业务功能。服务注册时把这个接口的地址也告诉注册中心,注册中心会定时调用这个接口来检测服务是否正常,如果不正常,则将它移除,这样就保证了服务的可用性。          app.Map("/HealthCheck", s =>          {              s.Run(async context =>              {                  await context.Response.WriteAsync("ok");              });          });          return app;      }  }

4.3APIServiceA项目

项目添加一个Get方法,对应APIGateway项目的路由上下游配置,具体代码如下:

[Route("api/[controller]")]  [ApiController]  public class ValuesController : ControllerBase  {      // GET api/values      [HttpGet]      public ActionResult<IEnumerable<string>> Get()      {          var port = Request.Host.Port;          return new string[] { "value1", "value2", port.Value.ToString() };      }  }

appsettings.json配置加入Consul地址:

"Consul": {    "Host": "http://192.168.113.128:8500"  }

4.4APIServiceB项目

项目添加一个Get方法,对应APIGateway项目的路由上下游配置,具体代码如下:

[Route("api/[controller]")]  [ApiController]  public class ValuesController : ControllerBase  {      // GET api/values      [HttpGet]      public ActionResult<IEnumerable<string>> Get()      {          var port = Request.Host.Port;          return new string[] { "value3", "value4", port.Value.ToString() };      }  }

appsettings.json配置加入Consul地址:

"Consul": {    "Host": "http://192.168.113.128:8500"  }

4.5项目运行

在APIServiceA、APIServiceB项目的ConfigureServices添加Consul配置:

services.AddConsulConfig(Configuration);

在Configure添加Consul服务注册:

APIServiceA:app.UseConsul("http://172.168.18.73", "9999");  APIServiceB:app.UseConsul("http://172.168.18.73", "9998");

把APIGateway、APIServiceA、APIServiceB三个项目部署到IIS上:

三个项目运行起来后,通过浏览器Consul客户端可以看到MyService节点服务情况:

点击打开MyService节点可以看到注册到Consul的APIServiceA、APIServiceB服务状况:

如果把APIServiceB服务实例站点停掉:

通过Consul客户端会看到APIServiceB服务实例已经被剔除了:

如果输入CTRL+C把集群中某一个Consul服务关闭,那么集群会重新选举一个新的leader,负责处理所有服务实例的查询和事务: