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

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

.NET 6 使用 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界面

.NET 6 使用 Consul 实现服务注册与发现

项目搭建

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

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

.NET 6 使用 Consul 实现服务注册与发现

在Service.Framework 中安装Consul NuGet包

.NET 6 使用 Consul 实现服务注册与发现

然后将 Consul 配置信息添加至各自项目的 appsettings.json 文件中

.NET 6 使用 Consul 实现服务注册与发现

因为我们要将项目都运行在docker中,所以这里的地址要用 host.docker.internal 代替,使用 localhost 无法正常启动,如果不在 docker 中运行,这里就配置层 localhost。

服务注册

配置Consul 服务注册

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

我们直接在Service.Framework 添加扩展方法ConsulExtend.cs

.NET 6 使用 Consul 实现服务注册与发现

Program.cs

.NET 6 使用 Consul 实现服务注册与发现

ConsulRegister.cs

.NET 6 使用 Consul 实现服务注册与发现

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

.NET 6 使用 Consul 实现服务注册与发现

配置健康检查

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

.NET 6 使用 Consul 实现服务注册与发现

这里健康检查地址需要与appsetting.json文件中配置的地址对应

.NET 6 使用 Consul 实现服务注册与发现

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

.NET 6 使用 Consul 实现服务注册与发现

增加测试接口

直接在Program.cs 直接添加即可

.NET 6 使用 Consul 实现服务注册与发现

增加docker支持

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

接下来我们通过 Visual Studio为ServiceA与ServiceB项目生成对应的Dockfile文件

 

.NET 6 使用 Consul 实现服务注册与发现

ServiceA.NET 6 使用 Consul 实现服务注册与发现

ServiceB

.NET 6 使用 Consul 实现服务注册与发现

编译镜像

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

.NET 6 使用 Consul 实现服务注册与发现

查看编译好的镜像

.NET 6 使用 Consul 实现服务注册与发现

运行镜像

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

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

分别执行命令

.NET 6 使用 Consul 实现服务注册与发现

然后访问 http://localhost:8500 查看Consul查看服务是否注册成功

.NET 6 使用 Consul 实现服务注册与发现

.NET 6 使用 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"

.NET 6 使用 Consul 实现服务注册与发现

.NET 6 使用 Consul 实现服务注册与发现

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

访问服务接口

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

.NET 6 使用 Consul 实现服务注册与发现

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

服务发现

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

代码很简单

.NET 6 使用 Consul 实现服务注册与发现

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

.NET 6 使用 Consul 实现服务注册与发现

.NET 6 使用 Consul 实现服务注册与发现

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

builder.Services.AddConsulDispatcher(ConsulDispatcherType.Polling);

.NET 6 使用 Consul 实现服务注册与发现

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

ServiceADiscoveryTest

.NET 6 使用 Consul 实现服务注册与发现

ServiceBDiscoveryTest

.NET 6 使用 Consul 实现服务注册与发现

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

结尾

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

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

代码地址

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