[apache/dubbo]一个消费端包含多个引用同一服务接口的DubboReference时,ReconnectTimerTask任务在服务端修订地址端口重新注册后仍然不断尝试连接旧地址

2024-07-02 897 views
4
Environment
  • Dubbo version: master 3.0 消费端存在多个DubboReference同一个服务接口
Steps to reproduce this issue
  1. 服务端配错了端口注册到了注册中心,但是防火墙没有开启消费端到服务端这个端口的策略
  2. 消费端从注册中心正常订阅到服务端的url,建立连接时被防火墙阻断,开始ReconnectTimerTask自动重连
  3. 服务修改端口后重新启动,又注册到注册中心了 但是消费端的ReconnectTimerTask依然锲而不舍地在重连旧url,原因是destroyUnusedInvokers销毁旧连接时,调用invoke.destroy方法只调用一次client.close,多个Reference同一接口时,ReferenceCountExchangeClient的referenceCount大于1,只close一次只是把referenceCount减一而已,不会实际关闭。 我想为Invoker接口加一个新的destroyAll接口,default实现是直接调用destroy,DubboInvoker这样的invoker自己override立即关闭客户端,提供给destroyUnusedInvokers方法调用,如何?

回答

4

我不确定为什么需要 destroyAll 接口:

  1. 如果 Directory.destroyAllInvokers() 被调用了,只是最终client关闭会稍微延迟一些,因为中间有一些invoker迭代过程,但这也符合未关闭的invoker的状态
  2. 如果只是 Directory.destroyUnusedInvokers() 被调用了,则会根据实际关闭的invoker数量决定client是否应该被关闭。

referenceCount 应该和 DubboInvoker 实例的数量严格相等,和reference多次没有关系吧?

6

referenceCount 应该和 DubboInvoker 实例的数量相等,只是你看看ServiceDiscoveryRegistryDirectory的urlInvokerMap结构,你就明白了。 这个map是当client收到注册中心notify时对URL相关的invoker进行更新的,key是URL,value是这个URL对应的其中一个invoker,但是这个URL对应的invoker并不都是由ServiceDiscoveryRegistryDirectory创建的,当初我曾尝试过把这个map的value搞成set集合,但是发现要伤筋动骨大改,涉及的内容太多了,就退回来搞了个destryAll。 ServiceDiscoveryRegistryDirectory里的urlInvokerMap保存的invoker只是收到注册中心服务实例通知时创建的invoker。

7

URL修改时,注册中心只会通知1次,destroyUnusedInvokers会被触发,只关一次,那么该URL有多个invoker时,这就会造成持续尝试连接旧URL的问题。

5

destroyAllInvokers被调用时,如果其中一个invoker的client是ReferenceCountExchangeClient,那么URL有多个invoker时,只做一次close,ReferenceCountExchangeClient也不会实际关闭,因为刚才说了,urlInvokerMap保存的invoker只是收到注册中心服务实例通知时创建的invoker,并不是全部invoker。

8

dubbp spi 会对 invoker 进行wrapper 如果wrapper 没有实现destroyAll的话还是只会调用默认的destroy方法。我们用3.0.5发现仍会报这个异常debug 发现urlInvokerMap的invoke实际类型为ProtocolListenerWrapper

7

@chickenlj @zrlw 帮忙看下这个问题呢,我们这边使用修复后的版本,发现问题还是存在,希望给一下修复建议。

6

之前说过,dubbo3.0.5增加的推空保护机制和更改URL断开旧连接的功能有冲突。 https://github.com/apache/dubbo/issues/9623 在所有客户端将推空保护机制( https://github.com/apache/dubbo/pull/9408 )选项配置成false应该能解决持续尝试旧连接的问题。

dubbo: 
   registry:
     enable-empty-protection: false

@furaul 可以试一下修改推空保护机制的相关代码,修改服务端URL时不做保护处理。

8

ServiceDiscoveryRegistryDirectory

@zrlw @chickenlj 辛苦看下这个问题吧,3.0.7仍然有这个问题,不是推空导致的,关闭推空机制没什么用,是因为dubbp spi 会对 invoker 进行wrapper 如果wrapper 没有实现destroyAll的话还是只会调用默认的destroy方法,堆栈:

image

9

@zrlw 问题还是存在, 3.0.6版本, 原因应该是@dlmu-lq所说。

7

@zrlw 这个问题我复现了下,本质上是由于 3.0 之前的版本在订阅 MetadataService 的时候只会创建不会销毁,导致多了一个 reference 在 MetadataService 上,进而导致服务端下线之后重连机制还在。

这个问题在 #10148 中已经修复,对应修复的版本号是 3.0.10。

这个 issue 中原本使用 destroyAll 的方式会导致如果多个服务订阅复用了连接,其中一个服务下线会关闭整个连接,进而导致连接抖动和出现 safe guard client , should not be called ,must have a bug 的报错。

目前接口级的 Directory 和应用级的 ServiceDirectory 都应该和 reference 数对应,如果不对应需要修复对应关系,从底层直接关闭连接在极端场景下带来的问题更严重。