.Net 6 使用 Consul 实现服务注册与发现 看这篇就够了
前言
在这几年的工作中,也经历过几个微服务项目,大多数都上了k8s,基本上都看不到Consul的身影,所以在工作之余折腾了下Consul,作为个人来讲还是得学习下,所以也就顺便通过文章来记录下学习过程。还有就是现在基本上只要提到微服务,其中涉及到的知识点就会很繁多,遇到的问题也会很多,建议阅读本文前先了解下“什么是Consul?”,”什么是注册中心、服务注册、服务发现?”,”为什么需要有服务注册与服务发现?”等等,然后可以参考大佬的文章,讲的非常详细了 Consul概念及其架构方式
然后本文只是个人学习与分享,不喜勿喷,谢谢。
什么是Consul?
这里只简单介绍下Consul,网上相关文章也比较多,就不过多陈述。
Consul官网:https://www.consul.io
开源地址:https://github.com/hashicorp/consul、https://github.com/G-Research/consuldotnet
Consul 作为一种分布式服务工具,为了避免单点故障常常以集群的方式进行部署,在 Consul 集群的节点中分为 Server和 Client两种节点(所有的节点也被称为Agent),Server 节点保存数据,Client 节点负责健康检查及转发数据请求到 Server;Server 节点有一个 Leader 节点和多个 Follower 节点,Leader 节点会将数据同步到 Follower 节点,在 Leader 节点挂掉的时候会启动选举机制产生一个新的 Leader。
Client 节点很轻量且无状态,它以 RPC 的方式向 Server 节点做读写请求的转发,此外也可以直接向 Server 节点发送读写请求。下面是 Consul 的架构图,很好的解释了Consul的工作原理。
环境准备
.Net 6
Docker desktop
Visual Studio 2022
安装Consul
Consul 支持各种平台的安装,安装文档:https://www.consul.io/downloads,为了快速使用,我这里选择用 docker 方式安装。
docker pull consul --默认拉取latest
docker run -d --name consul -p 8500:8500 consul:latest --使用镜像 consul:latest 启动容器,将容器的8500端口映射到主机的8500端口
启动Consul,打开默认地址 http://localhost:8500 可以看到Consul的UI界面
项目搭建
我们先准备2个Service(Minimal Api)以及一个Client(空 Web)
然后准备一个类库 Service.Framework 用于封装Consul IOC注册
在Service.Framework 中安装Consul NuGet包
然后将 Consul 配置信息添加至各自项目的 appsettings.json 文件中
因为我们要将项目都运行在docker中,所以这里的地址要用 host.docker.internal 代替,使用 localhost 无法正常启动,如果不在 docker 中运行,这里就配置层 localhost。
服务注册
配置Consul 服务注册
我们首先需要将ServiceA与ServiceB注册到Consul中
我们直接在Service.Framework 添加扩展方法
ConsulExtend.cs
Program.cs
ConsulRegister.cs
然后直接在对应Service项目进行IOC注册即可
配置健康检查
然后我们还需要配置对应健康检查,用来监控服务可用性,主动区分出不可用服务。
这里我们使用中间件的方式
在Service.Framework中添加HealthCheckMiddleware.cs
这里健康检查地址需要与appsetting.json文件中配置的地址对应
然后在对应服务项目中使用即可
Program.cs(示例代码为 ServiceA,ServiceB服务可以自行添加)
增加测试接口
直接在Program.cs 直接添加即可
增加docker支持
这里我因为选择的是在docker中运行,所以需要为项目添加docker支持。
接下来我们通过 Visual Studio为ServiceA与ServiceB项目生成对应的Dockfile文件
ServiceA
ServiceB
编译镜像
然后定位到项目根目录,使用命令去编译两个镜像,service_a和service_b(这里不清楚的可以参考我之前的文章 .NET 6 从0到1使用Docker部署至Linux环境)
docker image build -f ./ServiceA/Dockerfile -t service_a .
docker image build -f ./ServiceB/Dockerfile -t service_b .
查看编译好的镜像
运行镜像
接下来运行镜像启动项目实例
docker run -d -p 5050:80 --name service_a1 service_a --ConsulRegisterOptions:Port="5050"
docker run -d -p 5060:80 --name service_b1 service_b --ConsulRegisterOptions:Port="5060"
然后这里注意一下 ConsulRegisterOptions:Port="5050"
这里的意思是会替换appsetting.json 文件中的ConsulRegisterOptions配置文件中Port的内容,这里可以方便后面启动多实例时指定对应端口,本示例为单实例也可不需要此项内容,默认使用配置文件中的Port
docker run -d -p 5050:80 --name service_a1 service_a
docker run -d -p 5060:80 --name service_b1 service_b
分别执行命令
然后访问 http://localhost:8500 查看Consul查看服务是否注册成功
会发现服务注册成功,健康检查也已通过,后面服务地址端口也都对应。
服务实例集群
然后我们继续为服务启动多个实例
docker run -d -p 5051:80 --name service_a2 service_a --ConsulRegisterOptions:Port="5051"
docker run -d -p 5052:80 --name service_a3 service_a --ConsulRegisterOptions:Port="5052"
docker run -d -p 5061:80 --name service_b2 service_b --ConsulRegisterOptions:Port="5061"
docker run -d -p 5062:80 --name service_b3 service_b --ConsulRegisterOptions:Port="5062"
当然这里如果你觉得比较繁琐,也可以使用docker compose来编排脚本启动服务实例
访问服务接口
接下来我们试着访问访问接口,看看能不能出现效果
因为终端编码问题,导致显示乱码,这个不影响,ok,至此服务注册大功告成
服务发现
我们直接在Client项目的Program.cs中准备两个接口
代码很简单
下面这个接口,我把上面的代码封装了一下,弄了个抽象类,然后模拟了3种调度策略,意思一样的
用之前记得先在IOC注册一下
builder.Services.AddConsulDispatcher(ConsulDispatcherType.Polling);
然后我们分别访问两个接口,模拟访问3次,看下效果:
ServiceADiscoveryTest
ServiceBDiscoveryTest
至此,服务发现就大功告成,就算其中某个节点挂掉,服务也可以正常运行。
结尾
本文只是简单的实现了Consul的服务注册和发现,至于怎么更好的应用到实际项目,还得继续探索。
后面会继续学习网关等相关知识,感兴趣的同学欢迎继续关注!