深度剖析:Dubbo使用Nacos注册中心的坑
深度剖析:Dubbo使用Nacos注册中心的坑
本文将深入剖析在将Dubbo的注册中心从Zookeeper切换到Nacos时遇到的一个典型问题。通过详细描述问题现象、排查过程、技术分析和最终解决方案,帮助读者理解Dubbo与Nacos的交互机制,并掌握在遇到类似问题时的处理方法。
1. 问题描述
几年前在进行微服务部件升级时,将Dubbo的注册中心从Zookeeper切换到Nacos。切换的原因主要有两点:
- Zookeeper保障了CP,面对大量服务上下线时,吞吐量和响应有瓶颈。Nacos保障了AP,在当前微服务场景下,业界建议优先保障AP,以获得更好的吞吐量和响应速度。
- 本着能少用部件就少用的原则,Nacos既可以做注册中心也可以做配置中心,因此选择二合一,只采用一个部件。
切换后出现了两个主要问题:
- 部分微服务启动异常缓慢,甚至长达15分钟都无法成功启动,期间持续打印大量Nacos请求日志。而有些微服务启动则相对正常。
- 通过VisualVM查看JVM线程情况时发现,某些微服务的线程数高达4000左右,远高于切换前的几百个线程数。这导致CPU使用率显著升高,同时服务启动速度变慢。
2. 通过现象开始排查
面对这些问题,只能从常规手段入手逐步排查。根据表面现象,初步判断是Nacos的问题(尽管最终结论并非如此)。
现象一:微服务启动缓慢,持续打印大量Nacos请求日志。日志示例如下:
现象二:JVM线程数异常增多,达到4000左右。线程情况如下:
基于以上现象,推测可能由于某种原因产生了大量Nacos线程,每个线程都在不停地发送HTTP请求。
3. 分析Nacos
回顾Nacos原理
Nacos客户端注册和订阅服务的基本流程包括:
- 定时从Nacos服务器拉取服务信息的线程
- 维持心跳的线程
- 监听服务变更的线程
- 推送本服务变更信息的线程
在Nacos源码中,可以找到这些线程的具体实现:
进一步分析发现,这些线程确实都在向Nacos服务器发送对应的HTTP API请求:
4. Dubbo登场
在深入分析Dubbo源码之前,先回顾Dubbo基于引用配置文件或注解创建Proxy的基本流程:
- ReferenceAnnotationBeanPostProcessor#doGetInjectedBean
- ReferenceAnnotationBeanPostProcessor#buildReferenceBeanIfAbsent
- ReferenceBeanBuilder#build
- ReferenceBean#afterPropertiesSet
- ReferenceConfig#init
重点分析ReferenceConfig#init方法,其中有一行关键代码:
ref = createProxy(map);
继续追踪该代码,发现:
- RegistryProtocol#refer
- AbstractRegistryFactory#getRegistry
核心问题出现在NacosRegistryFactory中,具体如下:
问题根源在于:
- 在
ReferenceConfig#init方法中引入了timestamp参数 NacosRegistryFactory实现的createRegistryCacheKey方法没有处理这个timestamp参数- 导致无法从缓存中获取注册中心信息,从而不断创建新的线程并发送HTTP请求
查看ReferenceConfig#init方法的源码,确实加入了timestamp参数:
5. 解决方法
解决方案很简单:在Dubbo的NacosRegistryFactory类中处理掉timestamp参数。
遗憾的是,作者发现这个问题时,已经有其他网友在Dubbo官方仓库提交了相关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×tamp=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依赖包中的NacosRegistryFactory类替换为2.7.9版本的实现,问题得以解决。
6. 总结
本文详细梳理了Dubbo使用Nacos注册中心时遇到的一个典型问题,并展示了如何通过现象分析逐步找到问题本质的排查过程。这要求读者对Dubbo和Nacos有深入的理解。因此,平时积累技术知识、深入理解原理是非常必要的。