net core天马行空系列-微服务篇:全声明式http客户端feign快速接入微服务中心nacos


1.前言

hi,大家好,我是三合,距离上一篇博客已经过去了整整两年,这两年里,博主通关了《人生》这个游戏里的两大关卡,买房和结婚。最近闲了下来,那么当然要继续写博客了,今天这篇博客的主要内容是,net core/.net6中,如何利用SummerBoot(点我打开详情介绍)中的feign模块快速接入微服务中心nacos(点我打开详情介绍),接下来我会从微服务的概念到feign的使用方法,由浅入深的进行介绍。

2.服务与实例

服务与实例的关系,可以类比为程序员与你我的关系,程序员是服务,我们是实例,比如老板要实现一个功能,那么他一般会说,交给程序员开发,而不是说交给张三开发,万一张三离职了,他还要问还有谁是程序员,然后才能说找谁开发,太麻烦了,毕竟老板的重点是找人开发出来,而不是具体找哪个人,直接说交给程序员开发,那么无论公司里哪个程序员离职或者入职,只要程序员群体有人就行,服务在这里就起到了一个统一入口,屏蔽底层细节,减少沟通成本的作用。拉回正题,当我们的系统对外提供了api接口给其他系统调用的时候,我们就可以说这个系统对外提供了服务,当然,为了高可用性,我们一般会部署多个站点进行负载均衡,免得一个站点挂了,整个系统就挂了,在微服务中,我们一般称呼这一个个站点为一个个服务实例,服务与实例一般是一对多的关系。

3.nacos的安装

安装具体参考nacos官网,博主这里推荐使用docker的方式安装,简单又快速,免得大家迈出微服务第一步的时候就出师未捷身先死。

4.安装SummerBoot

在nuget中搜索Summerboot,安装即可,SummerBoot支持net core 3.1和.net 6。

5. 服务实例注册与服务调用

我们平时调用其他的系统的接口,一般都需要,ip(域名)+port+具体接口,但在nacos中,则可以简化为,服务名+具体接口,nacos中,服务是一等公民,我们调用的都是服务,至于哪些服务实例联合一起提供了这些服务,则不是我们关心的内容,换言之,nacos中,服务帮我们屏蔽了一个个站点的细节,我们不用去关心要调用的接口的ip地址和端口号。使用nacos服务中心,分为两步,服务实例注册和服务调用。

5.1 服务实例注册

我们以程序员的身份入职公司,这一步就是服务实例注册,这样我们就成为我们公司程序员群体中的一员了。拉回正题,服务实例注册就是将我们的应用注册为服务的一个实例,由服务作为对外统一入口,为其他系统提供接口服务,以供他们调用。这里提一下nacos检测实例是否存活的心跳机制就是实例每5秒钟向nacos注册中心发送一个心跳包,确认存活状态。那么在feign中是如何注册服务实例的呢,非常简单,只需要两步

5.1.1 在配置文件appsettings.json/appsettings.Development.json中添加nacos的配置信息

"nacos": {
    //nacos服务地址,如http://172.16.189.242:8848
    "serviceAddress": "http://172.16.189.242:8848/",
    //是否要把应用注册为服务实例
    "registerInstance": true,
    //命名空间id,如832e754e-e845-47db-8acc-46ae3819b638或者public
    "namespaceId": "dfd8de72-e5ec-4595-91d4-49382f500edf",
    //要注册的服务名
    "serviceName": "test",
    //服务的分组名
    "groupName": "DEFAULT_GROUP",
    //权重,一个服务下有多个实例,权重越高,访问到该实例的概率越大,比如有些实例所在的服务器配置高,那么权重就可以大一些,多引流到该实例,与上面的参数lbStrategy设置为WeightRandom搭配使用
    "weight": 1,
    //本应用对外的网络协议,http或https
    "protocol": "http",
    //本应用对外的端口号,比如5000
    "port": 5000
  }

每一个配置参数我都添加了详细的说明,大家注意这里,我们把应用对外的端口号和协议都配置了,为什么没配置ip呢,因为ip可以自动获取,就免得大家配置了,这里weight(即权重)参数是什么意思呢,我们平时部署应用的服务器的配置有的高,有的低,比如两台服务器,A和B,A是4c8g,B是128c256g,那么明显B服务器配置更高,我们就应该把更多的请求分配给B服务器去处理,毕竟能者多劳嘛,那么我们在A和B服务器上部署应用,配置weight参数时,A服务器上的应用应当配置为1,B配置为32,代表1台B服务器相当于32台A服务器,这些配置信息会作为元信息上传到nacos中心,这样我们在服务调用的时候才会知道,该用怎么样的方式,什么ip什么端口去请求哪个实例上的接口。

5.1.2 在StartUp.cs中添加配置

services.AddSummerBoot();
services.AddSummerBootFeign(it =>
{
	it.AddNacos(Configuration);
});

仅需这两步,我们就完成了服务实例注册,运行web程序,然后大家打开nacos的面板,就可以看到我们注册的服务,点开服务,也可以看到服务下面的实例,至此,我们的服务就可以对外提供服务了。同时启动多个应用就意味着注册了多个服务实例,注意要配置好相应的端口号。

5.2 服务调用

老板说把某个需求交给程序员实现,这就是服务调用。那么我们如何调用别的微服务系统所提供的服务呢?在feign中,这也是非常简单的,因为feign本身是一个声明式http客户端,我们只需要定义接口,实现类会由SummerBoot框架自动生成,我们在要用到的地方注入接口即可直接调用。feign服务调用分为三步。

5.2.1 在配置文件appsettings.json/appsettings.Development.json中添加nacos的配置信息

"nacos": {
    //nacos服务地址,如http://172.16.189.242:8848
    "serviceAddress": "http://172.16.189.242:8848/",
    //客户端负载均衡算法,一个服务下有多个实例,lbStrategy用来挑选服务下的实例,默认为Random(随机),也可以选择WeightRandom(根据服务权重加权后再随机)
    "lbStrategy": "Random",
  }

大家应该可以看到,相比于服务实例注册,我们要调用别的微服务系统所提供的服务,要配置的参数要少的多,只有2个,nacos服务地址和客户端负载均衡算法,我这里要解释一下什么叫客户端负载均衡算法,我们将多个应用注册为服务实例后,当我们要调用这个服务时,feign会先请求nacos的接口,获取这个服务下所有的健康且存活的实例,那么理论上我们请求这些实例中的任何一个都是可以的,那么如何挑选实例呢,这里就涉及到一个客户端负载均衡算法,避免服务器分配到的流量旱的旱死,涝的涝死。feign在这里总共实现了2种算法,1.Random算法,即随机从实例列表中挑选出一个实例去发起请求,根据统计学,当请求量够大的时候,命中每个实例的概率是相同的,所以随机算法也可以称为平均算法。2.WeightRandom算法,即根据服务实例权重加权后再随机,此时要联系上面对weight参数的讲解,A服务器上的应用的权重是1,B服务器上的应用的权重是32,那么如果根据random算法,分配给A和B的流量都是50%,这明显不合理,所以我们根据权重加权后再随机,1+32=33,那么此时命中A的概率为1/33,命中B的概率为32/33,这样就合理多了。

5.2.2 在StartUp.cs中添加配置

services.AddSummerBoot();
services.AddSummerBootFeign(it =>
{
	it.AddNacos(Configuration);
});

5.2.3 定义调用微服务的接口

普通的feign接口像下面这样定义即可。注意,如果返回值是类的话,该类需要具有无参构造函数,否则会反序列化失败。

[FeignClient(Url = "http://localhost:5001/home")]
public interface ITestFeign
    {
        [PostMapping("/form")]
        Task TestForm([Body(BodySerializationKind.Form)] Test tt);

        [GetMapping("/query")]
        Task TestQuery([Query] Test tt);     

        [PostMapping("/json")]
        Task TestJson([Body(BodySerializationKind.Json)] Test tt);
}

微服务的接口定义就要复杂一些,需要设置微服务的名称ServiceName,分组名称NacosGroupName(不填则默认DEFAULT_GROUP),命名空间NacosNamespaceId(不填则默认public),以及MicroServiceMode设为true即可。url不用配置,剩下的就和正常的feign接口一样。

[FeignClient( ServiceName = "test", MicroServiceMode = true,NacosGroupName = "DEFAULT_GROUP", NacosNamespaceId = "dfd8de72-e5ec-4595-91d4-49382f500edf")]
public interface IFeignService
{
	[GetMapping("/home/index")]
	Task TestGet();
}

同时ServiceName,NacosGroupName,NacosNamespaceId也支持从配置文件中读取,配置项用${}包裹即可,如配置文件中配置为

{
  "ServiceName": "test",
  "NacosGroupName": "DEFAULT_GROUP",
  "NacosNamespaceId": "dfd8de72-e5ec-4595-91d4-49382f500edf"
}

接口定义为

[FeignClient( ServiceName = "${ServiceName}", MicroServiceMode = true,NacosGroupName = "${NacosGroupName}", NacosNamespaceId = "${NacosNamespaceId}")]
public interface IFeignService
{
	[GetMapping("/home/index")]
	Task TestGet();
}

定义完接口,就可以在要用到的地方注入,然后直接调用了,是不是觉得很简单。

6.结尾

更多feign的用法,可参考SummerBoot文档,也可以加入QQ群:799648362反馈建议。同时,如果你觉得这篇文章还不错的话,请记得一键三连(推荐+关注+github star)