深度解析Dubbo使用Nacos注册中心的问题及解决方法

2020年,笔者在进行微服务部件升级时,将Dubbo的注册中心从Zookeeper切换到Nacos时遇到了一些问题。最近,Github上也有网友提到了类似的问题。在本文中,笔者将对这些问题进行梳理和总结。

1、问题描述

在之前的微服务部件升级中,笔者将Dubbo的注册中心从Zookeeper切换到Nacos。切换的原因主要有两点:

  • Zookeeper保障了CP,但在面对大量服务上下线时,存在吞吐量和响应速度的瓶颈。而Nacos保障了AP,在当前的微服务场景下,业界建议优先保障AP,以获得较好的吞吐量和较快的响应。
  • 为了尽量减少故障点,笔者希望尽量减少使用部件。Nacos既可以做注册中心也可以做配置中心,因此可以二合一,只采用一个部件。

然而,切换后出现了两个现象:

  • 部分微服务启动非常缓慢,甚至长达15分钟无法成功启动,一直在打印大量的Nacos请求日志。但是有些微服务的启动速度较快。
  • 通过VisualVM查看JVM的线程情况,发现有的微服务线程数高达4000左右,而在切换之前只有几百的线程数。大量线程的启动导致CPU负载增加,同时服务启动速度也变慢。

2、通过现象开始排查

出现这种问题时,刚开始有点不知所措,但只能从常规的手段一点点排查。通过表面的现象,逐步进行分析。

现象一: 微服务启动缓慢,一直在打印大量的Nacos请求日志。

现象二: JVM的线程数高达4000左右。

基于以上现象,初步判断是Nacos的问题(当然结论不是nacos的问题)。

初步猜测可能是由于某种原因,产生了大量的Nacos线程,每个线程又在不停地发送http请求。

接下来继续分析Nacos。

3、分析Nacos

回顾Nacos原理

我们知道Nacos客户端注册和订阅服务的流程大致如下:

因此,一般nacos-client有4个重要线程:

  • 定时从nacos-server拉取服务的线程
  • 维持心跳的线程
  • 监听服务变更的线程
  • 推送本服务变更信息的线程

分析Nacos

根据以上线程的名称和原理流程图,我们可以在nacos源码里找到对应的位置,代码如下:

翻看每个线程里执行的任务,确实能找到它们都在向nacos-server发送对应的http的api请求:

既然找到了创建线程和发起http调用的原因,那就继续查看是哪里调用的。

此时会自然想到dubbo了,因为dubbo采用nacos作为注册中心,自然要依赖nacos-client创建出nacos注册中心相关的类,然后从中获取到微服务的元数据信息。

4、Dubbo登场

在翻看Dubbo源码之前,先回顾下Dubbo是怎样基于引用配置文件或者引用配置注解创建Proxy的,大致流程如下:

ReferenceAnnotationBeanPostProcessor#doGetInjectedBean

ReferenceAnnotationBeanPostProcessor#buildReferenceBeanIfAbsent

ReferenceBeanBuilder#build

ReferenceBean#afterPropertiesSet

ReferenceConfig#init

至此完成了referenceProxy的创建。

重点看 ReferenceConfig#init 方法,方法里有一行代码: ref = createProxy(map); ,顺着这行代码往里走,如下:

RegistryProtocol#refer

AbstractRegistryFactory#getRegistry

重点来了 重点来了 重点来了 核心代码和注释见下图

总之是 :因为在上面 ReferenceConfig#init 方法里引入了 timestamp 参数,同时又因为 NacosRegistryFactory 又自己实现了一套 createRegistryCacheKey 方法,这个方法里没有截掉 timestamp 参数,所有就会导致从缓存里取不到注册中心信息,所有就会不停的去创建,从而又创建了更多的线程,从而发送了很多http请求。

再次查看 ReferenceConfig#init 方法的源码,确实是加入了 timestamp 参数:

至此问题的原因已经找到了,接下来就是如何解决了。

5、解决方法

解决方法也很简单,就是在Dubbo的 NacosRegistryFactory 类里面截掉 timestamp 参数。

遗憾的是,我当时发现了这个问题时,打算给Dubbo官方发issue的,发现已经有网友抢先一步发了issue,并且已经合并到2.7.9分支里了。

以下是解决方法的代码截图:

两个版本处理URL的结果如下:

2.7.8版本:
nacos://10.20.1.13:8848,10.20.1.14:8848,10.20.1.15:8848/org.apache.dubbo.registry.RegistryService?application=ehome-cloud&application.version=1.0&dubbo=2.0.2&interface=org.apache.dubbo.registry.RegistryService&namespace=dev-jzj&owner=ehome-cloud-owner&pid=21335&qos.enable=false&release=2.7.8&timestamp=1712545856489

2.7.9版本:
nacos://10.20.1.13:8848,10.20.1.14:8848,10.20.1.15:8848/org.apache.dubbo.registry.RegistryService?namespace=dev-jzj

这个问题是在dubbo的2.7.8版本出现的,最后通过将2.7.9的修复class替换了2.7.8的 NacosRegistryFactory class类,然后重新打了dubbo依赖包,问题得以解决。

有朋友会问:为什么不是引用2.7.9呢?因为我担心2.7.9有其他问题,所以做个class替换,然后继续用2.7.8是个较好的方式。

6、总结

本文主要梳理了Dubbo使用Nacos注册中心的问题,同时也讲述了,出现问题时,如何一步一步排查。透过现象结合源码,逐步找到问题的真相。

当然在排查之前,就需要对Dubbo和Nacos有一定的了解。所以各位朋友,在平时还是要多积累,多深入原理,这样遇到问题才能顺利解决。

本篇完结!欢迎点赞 关注 收藏!!!

原文链接: https://mp.weixin.qq.com/s/r4O4d2gAwA8LfJ1Ir98nmg

=====>>>>>>关于我<<<<<<======

标签:游戏攻略