使用Docker部署Consul集群并由Ocelot调用
关于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/
可以进到控制界面。
二、将服务注册到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
在容器内需要访问宿主主机运行的服务
Address
:Consul
服务的请求地址
"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
中我们配置的参数,用于注册
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; } }
然后创建一个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>());
启动项目就能看到服务已经注册
Consul提供了http api可以让我们进行查询、注册、接触注册等操作
http://127.0.0.1:8500/v1/health/service/service-a?passing
三、使用Ocelot调用
我们新建一个项目然后安装包
Install-Package Ocelot.Provider.Consul
然后注册服务
builder.Services.AddOcelot().AddPolly().AddConsul();
之后在配置文件中的GlobalConfiguration
节点下添加如下参数,这是必须的如果没有指定主机Host
和端口Port
将会使用Consul
默认的,Scheme
默认为http
,Type
说明此服务发现由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 }