Nacos作为Dubbo注册中心时Consumer更新Provider的机制

小鱼儿 2022-09-08 00:15 332阅读 0赞

目录

一、RegistryDirectory

二、NacosRegistry

三、NacosNamingService

四、HostReactor

1、立即获取更新

2、定时更新配置

五、EventDispatcher

六、PushReceiver

七、获取最新Provider实例的三种方式

八、新增加Provider的日志分析


一、RegistryDirectory

Consumer端启动时,Dubbo的RegistryDirectory中向Nacos发起订阅,并把自己做为回调通知的Listenering执行器:

watermark_type_ZHJvaWRzYW5zZmFsbGJhY2s_shadow_50_text_Q1NETiBA5Yav56uL5b2s_size_20_color_FFFFFF_t_70_g_se_x_16

当Nacos中注册的Provider实例有发生变化时,会及时的收到回调通知,RegistryDirectory.notify(List urls)方法会被调用,用于处理接收到的新的Provider实例:

watermark_type_ZHJvaWRzYW5zZmFsbGJhY2s_shadow_50_text_Q1NETiBA5Yav56uL5b2s_size_20_color_FFFFFF_t_70_g_se_x_16 1

二、NacosRegistry

RegistryDirectory的subscribe(URL url)方法通过NacosRegistry中的doSubscribe(…)方法,往Nacos服务端发起注册及获取Provider信息:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_Q1NETiBA5Yav56uL5b2s_size_43_color_FFFFFF_t_70_g_se_x_16

方法doSubscribe(URL, NotifyListener)中根据Consumer端的url生成对应的Provider服务名称,如此时consumer的url如下:

  1. consumer://192.168.22.198/cn.raysonblog.shopserviceprovider.service.RpcShopService?application=shop-service-consumer&category=providers,configurators,routers&dubbo=2.0.2&generic=false&interface=cn.raysonblog.shopserviceprovider.service.RpcShopService&lazy=false&logger=log4j2&methods=sayHello,getConfigServiceName&pid=397&qos.enable=false&release=2.7.3&side=consumer&sticky=false&timestamp=1629807903321

此时生成的Provider服务名称serviceNames为:

  1. providers:cn.raysonblog.shopserviceprovider.service.RpcShopService::

三、NacosNamingService

在方法doSubscribe(URL, NotifyListener, Set)中通过namingService.getAllInstances(serviceName)获取当前Consumer对应服务的所有Provider节点,namingService代表的类为NacosNamingService ,在该类中会通过getAllInstances(…)方法调用HostReactor的getServiceInfo方法获取ServiceInfo对象:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_Q1NETiBA5Yav56uL5b2s_size_51_color_FFFFFF_t_70_g_se_x_16

四、HostReactor

getServiceInfo(…)方法会判断本地是否已经有Provider节点信息,如果没有则通过updateServiceNow(…)立即从Nacos服务获取:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_Q1NETiBA5Yav56uL5b2s_size_48_color_FFFFFF_t_70_g_se_x_16 以下将立即获取更新及将更新加到定时任务中分别说明。

1、立即获取更新

updateServiceNow(…)方法中是通过HTTP请求向Nacos获取最新的配置信息:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_Q1NETiBA5Yav56uL5b2s_size_42_color_FFFFFF_t_70_g_se_x_16

此处的serverProxy对象是NamingProxy类的实例,queryList(…)方法:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_Q1NETiBA5Yav56uL5b2s_size_38_color_FFFFFF_t_70_g_se_x_16

会调用Nacose服务的接口“/nacos/v1/ns/instance/list”获取数据,传入的参数:

  • namespaceId:指定获取配置的命名空间,dubbo可以通过配置项dubbo.registry.address中的nacos地址中增加namespace参数进行指定,如下所示:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_Q1NETiBA5Yav56uL5b2s_size_31_color_FFFFFF_t_70_g_se_x_16

  • serviceName:当前Consumer应用的要获取的Provider的服务名称,Consumer自动生成,类似如下所示:

    DEFAULT_GROUP@@providers:cn.raysonblog.shopserviceprovider.service.RpcShopService::

  • clusters:暂时不清楚,代码中也没有注释,默认情况下是new了一个空的ArrayList,到这里时该参数的值ArrayList中的值按英文逗号分隔后的拼接:StringUtils.join(clusters, “,”),因而其值还是空字符串””;

  • udpPort:用于接收Nacos的UDP连接的端口,Nacos服务端往当前应用发送推送消息时,就往该端口推送,接收UDP的实现为PushReceiver,后面单独介绍该实现;
  • clientIp:当前应用所在服务器的IP地址,用于接收Nacos服务的UDP推送等;
  • healthOnly:用于指定是否只获取健康的Provider示例,默认值为false,跟了一下queryList方法的调用,共有三个地方且都在HostReactor中,传入的值都是false,因而目前还没有发现可以通过配置修改该值;

调用Nacos服务获取Provider信息的接口,可能成功也可能失败,如果失败则不更新Provider的信息;如果成功则会比较本地与从服务端获取的进行刷新时间的比较,以便于确认其是否为过期服务等验证,验证通过后才会使用新的Provider信息更新本地的Provider信息,如下HostReactor.processServiceJSON(String)方法所示:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_Q1NETiBA5Yav56uL5b2s_size_42_color_FFFFFF_t_70_g_se_x_16 1

2、定时更新配置

定时更新是方法scheduleUpdateIfAbsent(serviceName, clusters)调用其中的addTask方法增加到定时任务中:

  1. public synchronized ScheduledFuture<?> addTask(UpdateTask task) {
  2. return executor.schedule(task, DEFAULT_DELAY, TimeUnit.MILLISECONDS);
  3. }

初次执行周期延迟为DEFAULT_DELAY,默认值为1000,即1秒。

其中UpdateTask为真正执行的线程,检查是否有新的Provider节点信息,实现如下:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_Q1NETiBA5Yav56uL5b2s_size_40_color_FFFFFF_t_70_g_se_x_16

通过UpdateTask的线程实现可以得知,Nacos作为注册中心时,Consumer会通过HostReactor中的UpdateTask线程间隔10秒去服务端拉取最新的Provider信息实现的,作为对监听即时回调的补充,避免由于监听回调用处理失败导致不能够获取到完整的Provider实例的问题

五、EventDispatcher

HostReactor.processServiceJSON(String)方法会调用EventDispatcher.serviceChanged(ServiceInfo)方法,将变化的ServiceInfo加入到名为changedServices的LinkedBlockingQueue队列中,然后会通过EventDispatcher中的线程Notifier进行处理:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_Q1NETiBA5Yav56uL5b2s_size_44_color_FFFFFF_t_70_g_se_x_16

EventListener会调用Dubbo服务提供者变更通知NotifyListener的实现类RegistryDirectory,通知Dubbo Consumer端发起对这些可用Provider的连接,如果原来有连接也会重新发起连接,后面会有操作步骤及日志展示。

六、PushReceiver

PushReceiver为Consumer接收Nacos服务端UDP通知的监听器,以单独的线程进行监听,分为三种消息类型:dom、service及dump,dom及service消息类型都是服务端给Consumer发送消息,如Provider节点变更消息,然后通过hostReactor.processServiceJSON(…)处理这些信息,如下图红枉中的代码所示;dump消息类型为服务端希望获取Consumer的中现在的Provider信息:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_Q1NETiBA5Yav56uL5b2s_size_43_color_FFFFFF_t_70_g_se_x_16 1

但是不论我是增加新的Provider还是断掉Provider,该线程就从来没有读取到数据,说明Nacos服务端没有通过UDP消息的方式往客户端推送消息,那问题来了,Nacos服务端会在什么时候触发UDP通知呢?

七、获取最新Provider实例的三种方式

综上所述,Nacos做为Dubbo注册中心时,获取到最新的Provider实例有以下三种方式:

1、通过注册的回调处理的org.apache.dubbo.registry.integration.RegistryDirectory.notfity()方法,即时的接收实例变更的回调用;

2、通过线程com.alibaba.nacos.client.naming.core.HostReactor.UpdateTask定期的去查询当前应用的Provider实例,避免由于监听回调用处理失败导致不能够获取到完整的Provider实例的问题;

3、通过UDP服务com.alibaba.nacos.client.naming.core.PushReceiver线程,接收Provider实例变化的通知,虽然在本次调试中没有触发这样的请求,但是其应该是有触发机制的。

八、新增加Provider的日志分析

现在已经启动一台Provider以及一台Consumer,现在观察一下新增加Provider时,Consumer在获取到新的Provider,除了对新节点进行重连以下,是否会重新对已经存在Provider再次发起连接。

启动第二台Provider 2

先看一下启动第二个Provider节点的启动日志:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_Q1NETiBA5Yav56uL5b2s_size_34_color_FFFFFF_t_70_g_se_x_16

55分19秒启完成,55分20秒显示成功注册到Nacos。

查看Consumer此时的日志变化:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_Q1NETiBA5Yav56uL5b2s_size_34_color_FFFFFF_t_70_g_se_x_16 1

Consumer在55分21秒的时候显示收到Nacos的通知,并在同一秒的时间成功与Provider连接。

通过上面的日志可以看到,Provider在启动成功后的秒钟之内,Consumer收到通知并成功与Provider进行连接。

与此同时Provider 1的日志控制台有重新注册到Nacos的日志:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_Q1NETiBA5Yav56uL5b2s_size_34_color_FFFFFF_t_70_g_se_x_16 2

启动第三台Provider 3

先看一下启动第三个Provider节点的启动日志:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_Q1NETiBA5Yav56uL5b2s_size_34_color_FFFFFF_t_70_g_se_x_16 3

56分17秒启完成,56分18秒显示成功注册到Nacos。

查看Consumer此时的日志变化:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_Q1NETiBA5Yav56uL5b2s_size_33_color_FFFFFF_t_70_g_se_x_16

Consumer在56分21秒的时候显示收到Nacos的通知,并在同一秒的时间成功与Provider连接。

通过上面的日志可以看到,Provider在启动成功后的秒钟之内,Consumer收到通知并成功与Provider进行连接。

与此同时Provider 1的日志控制台有重新注册到Nacos的日志:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_Q1NETiBA5Yav56uL5b2s_size_33_color_FFFFFF_t_70_g_se_x_16 1

与此同时Provider 2的日志控制台有重新注册到Nacos的日志: watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_Q1NETiBA5Yav56uL5b2s_size_33_color_FFFFFF_t_70_g_se_x_16 2

通过以上几个Provider的加入及日志分析,说明Consumer有重新往原来已经建立了连接的Provider发起了建立连接的请求

发表评论

表情:
评论列表 (有 0 条评论,332人围观)

还没有评论,来说两句吧...

相关阅读