lvliuzhong opened a new issue, #10570: URL: https://github.com/apache/dubbo/issues/10570
<!-- If you need to report a security issue please visit https://github.com/apache/dubbo/security/policy --> 项目A消费项目B的同一个dubbo类,如果存在不同的@Reference写法,可能会导致对应B服务下线后,对应内部连接不关闭,导致A调用调用B服务出现异常,此问题必现。 如果一个项目A,通过rpc调用别人的项目B,比如dubbo类(ProductInfoFacade),如果在项目A中,存在这个类的两种@Reference写法,比如说一个是 @Reference 一个是 @Reference(injvm = false),都是同一个ProductInfoFacade,如果项目B的一台机器C关闭(还存在其他机器B项目机器开启),则项目A的ProductInfoFacade服务调用会有问题,对应C服务的 连接不会关闭。 ### Environment * Dubbo version: 2.7.4.1 2.7.15 ,注册中心是 nacos ,本地客户端1.1.4和2.1.0都试过 * Operating System version: Mac Linux * Java version: 1.8 ### Steps to reproduce this issue 1. 项目B是个服务比如ProductInfoFacade 的服务提供者,项目A是消费项目B的ProductInfoFacade服务,服务中存在两种@Reference 写法。比如一个Service里面是 @Reference,另外个Service中是 @Reference(injvm = false)。 2. 启动项目A和项目B服务,项目B多起几个服务(保证A能正常调用B服务)。关闭其中一台B服务的机器C,这个时候nacos会通知A服务去关闭这台C机器对应的所有dubbo消费者,正常会关闭C机器的netty连接,但是实际上由于有2个@Reference不同写法,会导致 org.apache.dubbo.rpc.protocol.dubbo.ReferenceCountExchangeClient 的 referenceCount 比正常时候多一个(每多一种这种同类的不同写法,就会比正常多一个),这样在关闭的时候,对应的netty连接不会关闭,因为ReferenceCountExchangeClient 的 close方法的 “referenceCount.decrementAndGet() <= 0” 不会小于等于0,导致最终不会关闭这个netty连接。后台有一个校验netty channel连接的任务“org.apache.dubbo.remoting.exchange.support.header.ReconnectTimerTask” (1分钟一次的)会一直重连这个netty连接,但是会失败走入到“logger.error("Fail to connect to " + channel, e);”(由于 对应的C机器已经关闭了,肯定是连接不上,会有很多异常日志),同时 带@Reference(injvm = false)注解的ProductInfoFacade调用会报错,提示连接已断开。"org.apache.dubbo.rpc.RpcException: Failed to invoke remote method: getXXXX. provider: dubbo://10.132.138.241:20880/com.xxx.ProductInfoFacade?xxxxx. cause: message can not send. because channel is closed ",目前测试,带@Reference 注解的服务可以正常调度。 3. 我们为什么加 @Reference(injvm = false)? 是由于我们这边并没有完全RPC化,存在 `https://github.com/apache/dubbo/issues/6776` 这个问题,这个问题也是2.7.8版本中解决 `https://github.com/apache/dubbo/issues/6224`在“org.apache.dubbo.config.spring.beans.factory.annotation.ReferenceAnnotationBeanPostProcessor.doGetInjectedBean”方法中添加了“prepareReferenceBean(referencedBeanName, referenceBean, localServiceBean);”导致的循环引用问题(“问题出现在 ServiceBean serviceBean = getServiceBean(referencedBeanName);”),代码如下 ``` private void prepareReferenceBean(String referencedBeanName, ReferenceBean referenceBean, boolean localServiceBean) { // Issue : https://github.com/apache/dubbo/issues/6224 if (localServiceBean) { // If the local @Service Bean exists referenceBean.setInjvm(Boolean.TRUE); exportServiceBeanIfNecessary(referencedBeanName); // If the referenced ServiceBean exits, export it immediately } } private void exportServiceBeanIfNecessary(String referencedBeanName) { if (existsServiceBean(referencedBeanName)) { **ServiceBean serviceBean = getServiceBean(referencedBeanName);** if (!serviceBean.isExported()) { serviceBean.export(); } } } ``` 问题具体是 A服务的某个dubbo服务的实现类,如(ProductInfoFacadeImpl,实现的ProductInfoFacade),引入了B服务的一个Service,该Service中又有 @Reference这个 ProductInfoFacade,导致A服务在启动的时候,会去初始化 其他类的 @Reference这个 ProductInfoFacade服务时,会去加载A服务本地的ProductInfoFacadeImpl服务,Spring加载的,会去依赖B服务的Service,然后再是里面的 ProductInfoFacade时,又走了一次这个exportServiceBeanIfNecessary方法,这个时候获取的ServiceBean对象是一个SpringBean的引用,未完成初始化完,因为当前服务还在初始化这个ProductInfoFacade的ServiceBean,所以后面我们加了一个 @Reference(injvm = false),这样的话,就不会走 prepareReferenceBean 里面的 exportServiceBeanIfNecessary方法,完全走RPC了,不走本地服务,避免了这种循环依赖问题。 -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected] --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
