关于consul的介绍就不写了百度就行,我们直接开干。

一、部署consul集群

拉取consul的镜像

docker pull consul

然后部署consul容器

 docker run --name consul1 -d -p 8500:8500 -p 8300:8300 -p 8301:8301 -p 8302:8302 -p 8600:8600 consul:latest agent -server -bootstrap-expect 2 -ui -bind='0.0.0.0' -client='0.0.0.0'

8500 http 端口,用于 http 接口和 web ui
8300 server rpc 端口,同一数据中心 consul server 之间通过该端口通信
8301 serf lan 端口,同一数据中心 consul client 通过该端口通信
8302 serf wan 端口,不同数据中心 consul server 通过该端口通信
8600 dns 端口,用于服务发现

-bbostrap-expect 2: 集群至少两台服务器,才能选举集群leader
-ui:运行 web 控制台
-bind: 监听网口,0.0.0.0 表示所有网口,如果不指定默认为127.0.0.1,则无法和容器通信
-client : 限制某些网口可以访问

-server:表示该节点是server节点,不声明的话默认为client节点,它们的不同是server节点持久化信息,但是client不持久化会转发给server节点,并且server节点会有leader节点进行健康检测和同步信息到其他server节点

然后我们获取该consul1容器的ip,使用如下语句

docker inspect --format '{{ .NetworkSettings.IPAddress }}' consul1

获取到了172.17.0.5

然后我们再新建另一个consul容器,使用join来加入第一个consul1容器的集群

docker run --name consul2 -d -p 18500:8500 -p 18300:8300 -p 18301:8301 -p 18302:8302 -p 18600:8600 consul:latest agent -server -ui -bind='0.0.0.0' -client='0.0.0.0' -join 172.17.0.5

 我就建两个节点能运行即可,server一般建3-5个,client没有上限,所以根据上面的语句自己建立即可。

然后我们访问地址http://localhost:8500/可以进到控制界面。

使用Docker部署Consul集群并由Ocelot调用

 二、将服务注册到Consul中

为测试方便我们使用-dev参数允许启动一个Consul服务。

docker run --name consul1 -d -p 8500:8500 -p 8300:8300 -p 8301:8301 -p 8302:8302 -p 8600:8600 consul:latest agent -server -ui -bind='0.0.0.0' -client='0.0.0.0' -dev

我们新建一个项目,然后下载Consul包

Install-Package Consul

 然后我们添加一个健康检查的接口

    [Route("[controller]/[action]")]
    [ApiController]
    public class HealthController : Controller
    {
        [HttpGet("/healthCheck")]
        public IActionResult Check() => Ok("ok");
    }

之后我们在appsetting.json中配置我们的Consul参数,这些参数我们用来注册服务的一些信息,参数解释如下:

ServiceName:是服务的名称,同一个服务名的服务将会注册到同一个服务下的实例

ServiceIP:服务请求的主机地址

ServicePort:服务请求的端口

ServiceHealthCheck:服务健康检测接口地址,此处是host.docker.internal是因为Consul在容器内需要访问宿主主机运行的服务

AddressConsul服务的请求地址

  "Consul": {
    "ServiceName": "service-a",
    "ServiceIP": "127.0.0.1",
    "ServicePort": 5001,
    "ServiceHealthCheck": "http://host.docker.internal:5001/healthCheck",
    "Address": "http://127.0.0.1:8500"
  }

之后我们创建一个类名叫ConsulOption,用于使用过Option模式加载appsetting.json中我们配置的参数,用于注册

使用Docker部署Consul集群并由Ocelot调用

    public class ConsulOption
    {
        /// <summary>
        /// 服务名称
        /// </summary>
        public string ServiceName { get; set; }
        /// <summary>
        /// 服务IP
        /// </summary>
        public string ServiceIP { get; set; }
        /// <summary>
        /// 服务端口
        /// </summary>
        public int ServicePort { get; set; }
        /// <summary>
        /// 服务健康检查地址
        /// </summary>
        public string ServiceHealthCheck { get; set; }
        /// <summary>
        /// Consul 地址
        /// </summary>
        public string Address { get; set; }
    }

View Code

然后创建一个Consul服务注册类ConsulBuilderExtensions,对将本服务推送到Consul中去,具体的参数解释在代码注释中了

public static class ConsulBuilderExtensions
    {
        public static IApplicationBuilder RegisterConsul(this IApplicationBuilder app, IHostApplicationLifetime lifetime, ConsulOption consulOption)
        {
            var consulClient = new ConsulClient(x =>
            {
                x.Address = new Uri(consulOption.Address);
            });
            var registration = new AgentServiceRegistration()
            {
                ID = Guid.NewGuid().ToString(),
                Name = consulOption.ServiceName,// 服务名
                Address = consulOption.ServiceIP, // 服务绑定IP
                Port = consulOption.ServicePort, // 服务绑定端口
                Check = new AgentServiceCheck()
                {
                    DeregisterCriticalServiceAfter = TimeSpan.FromSeconds(5),//服务启动多久后注册
                    Interval = TimeSpan.FromSeconds(10),//健康检查时间间隔
                    HTTP = consulOption.ServiceHealthCheck,//健康检查地址
                    Timeout = TimeSpan.FromSeconds(5)
                }
            };
            // 服务注册
            consulClient.Agent.ServiceRegister(registration).Wait();
            // 应用程序终止时,服务取消注册

            lifetime.ApplicationStopping.Register(() =>
            {
                consulClient.Agent.ServiceDeregister(registration.ID).Wait();
            });
            return app;

        }
    }

最后我们注入本服务

builder.Services.AddSingleton(builder.Configuration.GetSection("Consul").Get<ConsulOption>());
//....
app.RegisterConsul(app.Lifetime, app.Services.GetRequiredService<ConsulOption>());

启动项目就能看到服务已经注册

使用Docker部署Consul集群并由Ocelot调用

Consul提供了http api可以让我们进行查询、注册、接触注册等操作

http://127.0.0.1:8500/v1/health/service/service-a?passing

使用Docker部署Consul集群并由Ocelot调用

 三、使用Ocelot调用

我们新建一个项目然后安装包

Install-Package Ocelot.Provider.Consul

然后注册服务

builder.Services.AddOcelot().AddPolly().AddConsul();

之后在配置文件中的GlobalConfiguration节点下添加如下参数,这是必须的如果没有指定主机Host和端口Port将会使用Consul默认的,Scheme默认为httpType说明此服务发现由Consul提供

"ServiceDiscoveryProvider": {
    "Scheme": "http",
    "Host": "localhost",
    "Port": 8500,
    "Type": "Consul"
}

 然后我们设置路由,添加我们刚注册的服务,最好是配合负载均衡参数咯,我这里没写

  "Routes": [
    {
      "DownstreamPathTemplate": "/{everything}",
      "DownstreamScheme": "http",
      "ServiceName": "service-a",
      "UpstreamPathTemplate": "/api/{everything}",
      "UpstreamHttpMethod": [ "Get", "Post" ]
    }

然后运行就可以看到结果了,需要注意的是请求是http还是https这些需要注意,我就卡在了这里很久,请求路径需要注意噢

最后,Ocelot是每次请求都去Consul获取最新的服务,如果需要设置间隔多久去获取Consul的最新服务(可能会有微小的性能改进,但是不知道原有服务是否可用,可能会有错误的返回噢)可以如下设置:

"ServiceDiscoveryProvider": {
    "Host": "localhost",
    "Port": 8500,
    "Type": "PollConsul",
    "PollingInterval": 100
}