.Net 6 使用 Consul 实现服务注册与发现 看这篇就够了

发布时间 2023-03-22 19:08:00作者: Mamba24⁸

前言

在这几年的工作中,也经历过几个微服务项目,大多数都上了k8s,基本上都看不到Consul的身影,所以在工作之余折腾了下Consul,作为个人来讲还是得学习下,所以也就顺便通过文章来记录下学习过程。还有就是现在基本上只要提到微服务,其中涉及到的知识点就会很繁多,遇到的问题也会很多,建议阅读本文前先了解下“什么是Consul?”,”什么是注册中心、服务注册、服务发现?”,”为什么需要有服务注册与服务发现?”等等,然后可以参考大佬的文章,讲的非常详细了 Consul概念及其架构方式
然后本文只是个人学习与分享,不喜勿喷,谢谢。

什么是Consul?

这里只简单介绍下Consul,网上相关文章也比较多,就不过多陈述
Consul官网:https://www.consul.io
开源地址:https://github.com/hashicorp/consulhttps://github.com/G-Research/consuldotnet

Consul 作为一种分布式服务工具,为了避免单点故障常常以集群的方式进行部署,在 Consul 集群的节点中分为 ServerClient两种节点(所有的节点也被称为Agent),Server 节点保存数据,Client 节点负责健康检查及转发数据请求到 Server;Server 节点有一个 Leader 节点和多个 Follower 节点,Leader 节点会将数据同步到 Follower 节点,在 Leader 节点挂掉的时候会启动选举机制产生一个新的 Leader。

Client 节点很轻量且无状态,它以 RPC 的方式向 Server 节点做读写请求的转发,此外也可以直接向 Server 节点发送读写请求。下面是 Consul 的架构图:

Consul 作为一种分布式服务工具,为了避免单点故障常常以集群的方式进行部署,在 Consul 集群的节点中分为 Server 和 Client 两种节点(所有的节点也被称为Agent),Server 节点保存数据,Client 节点负责健康检查及转发数据请求到 Server;Server 节点有一个 Leader 节点和多个 Follower 节点,Leader 节点会将数据同步到 Follower 节点,在 Leader 节点挂掉的时候会启动选举机制产生一个新的 Leader。

Client 节点很轻量且无状态,它以 RPC 的方式向 Server 节点做读写请求的转发,此外也可以直接向 Server 节点发送读写请求。下面是 Consul 的架构图,很好的解释了Consul的工作原理。

pasted-124.png

环境准备

.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界面
image 100.png

项目搭建

我们先准备2个Service(Minimal Api)以及一个Client(空 Web)

然后准备一个类库 Service.Framework 用于封装Consul IOC注册

image - 2023-01-06T171915.795.png

在Service.Framework 中安装Consul NuGet包
image - 2023-01-06T171937.761.png

然后将 Consul 配置信息添加至各自项目的 appsettings.json 文件中
image - 2023-01-06T172002.522.png
因为我们要将项目都运行在docker中,所以这里的地址要用 host.docker.internal 代替,使用 localhost 无法正常启动,如果不在 docker 中运行,这里就配置层 localhost。

服务注册

配置Consul 服务注册

我们首先需要将ServiceA与ServiceB注册到Consul中

我们直接在Service.Framework 添加扩展方法
ConsulExtend.cs
image - 2023-01-06T172019.627.png

Program.cs
image - 2023-01-06T172043.818.png

ConsulRegister.cs
image - 2023-01-06T172124.482.png

然后直接在对应Service项目进行IOC注册即可

image - 2023-01-06T172155.711.png

配置健康检查

然后我们还需要配置对应健康检查,用来监控服务可用性,主动区分出不可用服务。
这里我们使用中间件的方式
在Service.Framework中添加HealthCheckMiddleware.cs
image - 2023-01-06T172247.088.png

这里健康检查地址需要与appsetting.json文件中配置的地址对应
image - 2023-01-06T172302.930.png

然后在对应服务项目中使用即可
Program.cs(示例代码为 ServiceA,ServiceB服务可以自行添加)
image - 2023-01-06T172320.358.png

增加测试接口

直接在Program.cs 直接添加即可
image - 2023-01-06T172405.273.png

增加docker支持

这里我因为选择的是在docker中运行,所以需要为项目添加docker支持。

接下来我们通过 Visual Studio为ServiceA与ServiceB项目生成对应的Dockfile文件
image - 2023-01-06T172419.874.png

ServiceA
image - 2023-01-06T172451.611.png

ServiceB
image - 2023-01-06T172511.183.png

编译镜像

然后定位到项目根目录,使用命令去编译两个镜像,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 .

image - 2023-01-06T172607.293.png

image - 2023-01-06T172623.116.png

查看编译好的镜像
image - 2023-01-06T172647.537.png

运行镜像

接下来运行镜像启动项目实例

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

分别执行命令
image - 2023-01-06T172804.135.png

image - 2023-01-06T172822.110.png

然后访问 http://localhost:8500 查看Consul查看服务是否注册成功
image - 2023-01-06T172843.274.png

image - 2023-01-06T172859.225.png

image - 2023-01-06T173039.696.png

会发现服务注册成功,健康检查也已通过,后面服务地址端口也都对应。

服务实例集群

然后我们继续为服务启动多个实例

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"

image - 2023-01-06T173117.874.png

image - 2023-01-06T173132.859.png

image - 2023-01-06T173149.865.png

当然这里如果你觉得比较繁琐,也可以使用docker compose来编排脚本启动服务实例

访问服务接口

接下来我们试着访问访问接口,看看能不能出现效果

image - 2023-01-06T173211.994.png

因为终端编码问题,导致显示乱码,这个不影响,ok,至此服务注册大功告成

服务发现

我们直接在Client项目的Program.cs中准备两个接口

代码很简单
image - 2023-01-06T173234.124.png

下面这个接口,我把上面的代码封装了一下,弄了个抽象类,然后模拟了3种调度策略,意思一样的

image - 2023-01-06T173248.258.png

image - 2023-01-06T173311.441.png

用之前记得先在IOC注册一下

builder.Services.AddConsulDispatcher(ConsulDispatcherType.Polling);

image - 2023-01-06T173340.067.png

然后我们分别访问两个接口,模拟访问3次,看下效果:

ServiceADiscoveryTest

image - 2023-01-06T173406.898.png

image - 2023-01-06T173422.438.png

image - 2023-01-06T173439.187.png

ServiceBDiscoveryTest

image - 2023-01-06T173457.278.png

image - 2023-01-06T173510.990.png

image - 2023-01-06T173537.945.png

至此,服务发现就大功告成,就算其中某个节点挂掉,服务也可以正常运行。

结尾

本文只是简单的实现了Consul的服务注册和发现,至于怎么更好的应用到实际项目,还得继续探索。

后面会继续学习网关等相关知识,感兴趣的同学欢迎继续关注!

代码仓库地址

https://github.com/fengzhonghao8-24/ConsulIntroduction