.NET微服務從0到1:服務註冊與發現(Consul)

Consul搭建

基於Docker搭建Consul

以下為單機環境構建腳本,用於本機測試,生產環境中應當進行集群搭建

version: '3'  services:    consul:      image: consul:1.7.1      container_name: consul      volumes:        - /c/docker/consul/data:/consul/data        - /c/docker/consul:/consul/config      ports:        - 8300:8300        - 8301:8301        - 8301:8301/udp        - 8302:8302        - 8302:8302/udp        - 8400:8400        - 8500:8500        - 53:53/udp      command: agent -server -bind=0.0.0.0 -client=0.0.0.0 -node=consul_Server1 -bootstrap-expect=1 -ui  

成功搭建後,訪問8500埠,你可以看到如下介面
在這裡插入圖片描述

基於Windows搭建Consul

點擊下載Consul

執行以下命令運行consul

consul agent -dev

ServiceA集成Consul做服務註冊

配置前一篇文章中的ServiceA

Install-Package Consul -Version 0.7.2.6

  • Consul配置模型
public class ServiceDisvoveryOptions  {      public string ServiceName { get; set; }        public ConsulOptions Consul { get; set; }  }    public class ConsulOptions  {      public string HttpEndpoint { get; set; }        public DnsEndpoint DnsEndpoint { get; set; }  }    public class DnsEndpoint  {      public string Address { get; set; }        public int Port { get; set; }        public IPEndPoint ToIPEndPoint()      {          return new IPEndPoint(IPAddress.Parse(Address), Port);      }  }
  • 添加appsetting.json
  "ServiceDiscovery": {      "ServiceName": "ServiceA",      "Consul": {        "HttpEndpoint": "http://127.0.0.1:8500",        "DnsEndpoint": {          "Address": "127.0.0.1",          "Port": 8600        }      }    }
  • Consul服務註冊實現
private void ConfigureConsul(IApplicationBuilder app, IOptions<ServiceDisvoveryOptions> serviceOptions, IConsulClient consul, IHostApplicationLifetime lifetime)  {      var features = app.Properties["server.Features"] as FeatureCollection;      var addresses = features.Get<IServerAddressesFeature>()          .Addresses          .Select(p => new Uri(p));        foreach (var address in addresses)      {          var serviceId = $"{serviceOptions.Value.ServiceName}_{address.Host}:{address.Port}";            var httpCheck = new AgentServiceCheck()          {              DeregisterCriticalServiceAfter = TimeSpan.FromMinutes(1),              Interval = TimeSpan.FromSeconds(30),              HTTP = new Uri(address, "health").OriginalString          };            var registration = new AgentServiceRegistration()          {              Checks = new[] { httpCheck },              Address = address.Host,              ID = serviceId,              Name = serviceOptions.Value.ServiceName,              Port = address.Port          };            consul.Agent.ServiceRegister(registration).GetAwaiter().GetResult();            lifetime.ApplicationStopping.Register(() =>          {              consul.Agent.ServiceDeregister(serviceId).GetAwaiter().GetResult();          });      }  }
  • 配置服務到DI容器
public void ConfigureServices(IServiceCollection services)  {      services.AddOptions();      services.Configure<ServiceDisvoveryOptions>(Configuration.GetSection("ServiceDiscovery"));        services.AddSingleton<IConsulClient>(p => new ConsulClient(cfg =>      {          var serviceConfiguration = p.GetRequiredService<IOptions<ServiceDisvoveryOptions>>().Value;            if (!string.IsNullOrEmpty(serviceConfiguration.Consul.HttpEndpoint))          {              // if not configured, the client will use the default value "127.0.0.1:8500"              cfg.Address = new Uri(serviceConfiguration.Consul.HttpEndpoint);          }      }));        services.AddHealthChecks();      services.AddControllers();  }
  • 將Consul相關配置添加到管道
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IOptions<ServiceDisvoveryOptions> serviceDisvoveryOptions, IConsulClient consul, IHostApplicationLifetime lifetime)  {      if (env.IsDevelopment())      {          app.UseDeveloperExceptionPage();      }        app.UseRouting();        app.UseAuthorization();        ConfigureConsul(app, serviceDisvoveryOptions, consul, lifetime);        app.UseHealthChecks("/health");        app.UseEndpoints(endpoints =>      {          endpoints.MapControllers();      });  }
  • 運行ServiceA
    在這裡插入圖片描述
    可以看到健康檢查已通過,並且服務ServiceA已成功註冊到Consul

Ocelot集成Consul做服務發現

安裝nuget包依賴

Install-Package Ocelot.Provider.Consul -Version 14.0.11

在上一篇文章的基礎上,我們先修改ocelot.json
刪除了ServiceA路由中的DownstreamHostAndPorts寫死的地址和埠,改為使用Consul服務發現

{    "ReRoutes": [      {        "DownstreamPathTemplate": "/{url}",        "DownstreamScheme": "http",        "UpstreamPathTemplate": "/service-a/{url}",        "UpstreamHttpMethod": [ "Get", "Post" ],        "AuthenticationOptions": {          "AuthenticationProviderKey": "SampleKey",          "AllowedScopes": [ "gateway_api" ]        },        "ServiceName": "ServiceA",        "LoadBalancerOptions": {          "Type": "LeastConnection"        }      }    ],    "GlobalConfiguration": {      "BaseUrl": "http://localhost",      "ServiceDiscoveryProvider": {        "Host": "localhost",        "Port": 8500,        "Type": "Consul"      }    }  }

向容器中添加Consul服務

public void ConfigureServices(IServiceCollection services)  {      services.AddOcelot()          .AddConsul();  }

啟動各個服務,查看結果
在這裡插入圖片描述
可以看到已成功執行

更多參考

Consul
Service Discovery And Health Checks In ASP.NET Core With Consul