Dubbo 2.7 版本增长新特性,新系统开始使用 Dubbo 2.7.1 尝鲜新功能。使用过程当中不慎踩到这个版本的 Bug。git
Spring Boot 2.14-Release + Dubbo 2.7.1github
Dubbo 服务者启动成功,正常提供服务,消费者调用偶现失败的状况。错误以下图:面试
能够看出,主要缘由为 cause: message can not send, because channel is closed。 可是检查提供者,却发现服务进程正常。apache
登录 Dubbo admin 查看提供者服务,发现这个服务存在两个节点。服务器
192.168.164.77 为测试服务器的 ip,提供者位于这台机器,而另外一个 10.20.80.67 倒是本地电脑的 IP,可是此时本地并未运行这个服务。架构
再次查看服务报错的缘由,能够看到提供者调用l本地提供 RPC 的服务。因为本地服务已中止,致使调用失败。app
这个问题在以前版本从未碰到,刚开始隐约记得 Dubbo 服务提供者注册使用 ZooKeeper 临时节点,服务断开,会删除该节点。ide
在 Dubbo 主页搜索相关 issue,看到一样的问题 Dubbo-2.7.1 providers 重复注册.源码分析
查看相关回复,能够看到问题主要因为 dynamic 默认值变成 false ,而 2.7.1 以前版本默认不赋值,初始值为 null。测试
后续 PR 中已修复该问题 Fix issue 3785,修复代码将 dynamic 默认设置成 true。可是截止 20190515 该版本暂未发布。
知道问题缘由,这里咱们从源码分析一下,为何 dynamic 设置成 false 会致使该问题。
注:下面分析的是 Dubbo 2.7.1 的源码
下面咱们使用 Dubbo xml 配置相关。
在 xml 配置中,能够在如下两个地方设置 dynamic 属性。
服务启动时将会使用 DubboNamespaceHandler 解析,注入 Spring 容器。
其中会将 provider 标签解析成 ProviderConfig 对象,service 标签解析成 ServiceBean 对象。
查看继承关系,能够看到以上两个类都继承 AbstractServiceConfig , dynamic 位于这个父对象中。
能够看到该字段默认值为 false。
接着查看 Dubbo 服务导出过程,位于 ServiceBean#export,略过其余代码,咱们直接跳到关键 ServiceConfig#doExportUrlsFor1Protocol 。
能够看到这里调用了屡次 appendParameters 方法。 这个方法将利用反射,获取对象的中全部字段信息,而后添加到 map 中。其中字段名字为键值,字段实际值为内容。此时 map 键值内容为:
能够看到 map 中还有一个 default.dynamic,你们翻看代码本身思考一下,为何会出现这个?
接着咱们跳到后面:
在这里会将上面获得 map 组装到 URL 对象中,而后再注册到注册中心。。
因为注册中心使用的是 ZooKeeper,因此这里将会使用 ZookeeperRegistry 实现类。
首先查看 url##getParameter 方法,这里 Constants.DYNAMIC_KEY 值为 dynamic。
该方法会先从 parameters 中根据键值取值。若不存在,会再根据 default 做为前缀拼接再次取值。若还不存在则使用传入的默认值。
查看此时的 parameters 对象。
url.getParameter(Constants.DYNAMIC_KEY, true) 返回为 false。
而后分析 zkClient#create 方法,
因为 ephemeral 为 false,因此这个服务注册到 ZooKeeper 的节点为持久节点。
临时节点,客户端断开,会话超时后,ZooKeeper 将会自动删除这个节点。zookeeper-faq
面试题:服务提供者能实现失效踢出是什么原理(高频题)
服务宕机的时候,该节点因为是持久节点会永远存在,并且当服务再次重启的时候会将从新注册一个新节点。这样就致使 ZooKeeper 中存在额外失效的节点,且该节点还没法天然消除(除非手动调用 ZooKeeper 删除节点方法)。
因为 Dubbo 2.7.2 暂未发布,因此建议若想使用 Dubbo 2.7 新功能的同窗,使用 2.7.0 版本。若如今正在使用 2.7.2 版本,也不要慌张。只要服务不是异常宕机或者使用 kill -9 强制杀死进程,以上的现象将不会碰到。正常服务关闭的时候,Dubbo 服务会主动去 ZooKeeper 注销该服务,并删除这个节点。
还未使用该版本的同窗们,建议使用 2.7.0 或者等 2.7.2 发布之后,再使用。