同时使用注解和 xml 的方式引用 dubbo 服务产生的异常问题排查实战

一、现象

使用 nacos 作注册中心的线上 dubbo 消费端应用每隔 1 分钟就会抛出以下异常(为使描述简单化,文章中使用本地 demo 来复现),该异常表示无法连接到 172.17.0.1:20881 这台提供端

21:11:49.843 [dubbo-client-idleCheck-thread-1] ERROR org.apache.dubbo.remoting.exchange.support.header.ReconnectTimerTask.doTask(51) -  [DUBBO] Fail to connect to HeaderExchangeClient [channel=org.apache.dubbo.remoting.transport.netty4.NettyClient [172.17.0.1:0 -> /172.17.0.1:20881]], dubbo version: 2.7.3, current host: 172.17.0.1
org.apache.dubbo.remoting.RemotingException: client(url: dubbo://172.17.0.1:20881/com.example.dubbo.HelloService?anyhost=true&application=dubbo-consumer-app&bean.name=com.example.dubbo.HelloService&category=providers&check=false&codec=dubbo&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&heartbeat=60000&interface=com.example.dubbo.HelloService&lazy=false&methods=sayHello&path=com.example.dubbo.HelloService&pid=56029&protocol=dubbo&register=true&register.ip=172.17.0.1&release=2.7.3&remote.application=dubbo-provider-app&server=netty4&side=consumer&sticky=false&timestamp=1683113373687) failed to connect to server /172.17.0.1:20881, error message is:拒绝连接: /172.17.0.1:20881
	at org.apache.dubbo.remoting.transport.netty4.NettyClient.doConnect(NettyClient.java:166)
	at org.apache.dubbo.remoting.transport.AbstractClient.connect(AbstractClient.java:190)
	at org.apache.dubbo.remoting.transport.AbstractClient.reconnect(AbstractClient.java:246)
	at org.apache.dubbo.remoting.exchange.support.header.HeaderExchangeClient.reconnect(HeaderExchangeClient.java:155)
	at org.apache.dubbo.remoting.exchange.support.header.ReconnectTimerTask.doTask(ReconnectTimerTask.java:49)
	at org.apache.dubbo.remoting.exchange.support.header.AbstractTimerTask.run(AbstractTimerTask.java:87)
	at org.apache.dubbo.common.timer.HashedWheelTimer$HashedWheelTimeout.expire(HashedWheelTimer.java:648)
	at org.apache.dubbo.common.timer.HashedWheelTimer$HashedWheelBucket.expireTimeouts(HashedWheelTimer.java:727)
	at org.apache.dubbo.common.timer.HashedWheelTimer$Worker.run(HashedWheelTimer.java:449)
	at java.lang.Thread.run(Thread.java:748)
Caused by: io.netty.channel.AbstractChannel$AnnotatedConnectException: 拒绝连接: /172.17.0.1:20881
	at sun.nio.ch.SocketChannelImpl.checkConnect(Native Method)
	at sun.nio.ch.SocketChannelImpl.finishConnect(SocketChannelImpl.java:717)
	at io.netty.channel.socket.nio.NioSocketChannel.doFinishConnect(NioSocketChannel.java:325)
	at io.netty.channel.nio.AbstractNioChannel$AbstractNioUnsafe.finishConnect(AbstractNioChannel.java:340)
	at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:635)
	at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:582)
	at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:499)
	at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:461)
	at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:884)
	at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
	... 1 more
Caused by: java.net.ConnectException: 拒绝连接
	... 11 more

二、问题排查

经过确认,172.17.0.1:20881 这台提供端确实已经下线,所以才会无法连接。

通过查询 dubbo 源码得知,ReconnectTimerTask 是一个定时任务,其实例被 DubboInvoker 实例持有,当 DubboInvoker 对应的消费端服务与对端的提供端服务断开连接时,该定时任务就会定时重连提供端。正常情况下不会出现这种异常,因为提供端下线以后,会通过注册中心通知到消费端,消费端对应的 DubboInvoker 实例会被销毁,从而重连定时任务也会被销毁。

找到出问题的 HelloService 在应用中的使用方式,发现既有通过 @Reference 注解的方式使用,又有通过 xml 配置的方式使用。首先,将 arthas 连接到出问题的消费端应用,然后执行以下命令

ognl '#context=@org.apache.dubbo.config.spring.extension.SpringExtensionFactory@CONTEXTS.iterator.next, #context.getBean("helloService").handler.invoker.directory.urlInvokerMap'

得到结果为

在这里插入图片描述

发现 HelloService 服务的 RegistryDirectory 实例中确实还存在已经下线的提供端,说明提供端下线后该消费端没有感知到

接着,使用 arthas 监控看看消费端的服务监听 notify 方法是否生效,运行以下命令

watch org.apache.dubbo.registry.support.FailbackRegistry notify params

然后,将 20880 那台服务提供端也下线,看看 watch 命令的输出,如下所示

在这里插入图片描述

说明服务下线通知功能是正常的,不过这里的 RegistryDirectory 实例和上面使用 ognl 表达式查询的 RegistryDirectory 实例不是同一个,使用 ognl 表达式查询的实例如下

在这里插入图片描述

说明 HelloService 服务被 refer 了两次,也就是生成了两个服务引用,但是只有一个引用的服务监听生效。

三、结论

联系到 HelloService 在应用中有注解和 xml 两种引用方式,调试一遍服务引用的创建过程,发现在 NacosRegistry 中有这么一段服务订阅代码

在这里插入图片描述

这段代码的意思是,当相同的服务引用被创建了两次,只有第一次创建的引用会订阅服务。所以通过注解和 xml 两种方式创建 HelloService 服务引用时,会导致其中一个引用不会订阅服务变更,导致无法感知提供端下线,就会出现不断重连提供端的现象。

四、解决方案

应用中应当禁止同时使用注解和 xml 的方式来引用 dubbo 服务。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
使用Dubbo注解方式暴露接口,需要按照以下步骤进行: 1. 在Dubbo配置文件中,添加`<dubbo:annotation package="com.example.service"/>`,指定需要扫描的包名。 2. 在需要暴露的接口上添加`@Service`注解,该注解来自Dubbo。 3. 在Dubbo配置文件中,添加`<dubbo:protocol name="dubbo" port="20880"/>`,指定Dubbo使用的协议和端口。 4. 启动Dubbo服务。 下面是一个示例代码: 1. 服务接口 ```java public interface HelloService { String sayHello(String name); } ``` 2. 服务实现类 ```java @Service public class HelloServiceImpl implements HelloService { public String sayHello(String name) { return "Hello, " + name; } } ``` 3. Dubbo配置文件 ```xml <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://dubbo.apache.org/schema/dubbo" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd"> <!-- 指定需要扫描的包名 --> <dubbo:annotation package="com.example.service"/> <!-- 指定Dubbo使用的协议和端口 --> <dubbo:protocol name="dubbo" port="20880"/> <!-- 暴露服务 --> <bean id="helloService" class="com.example.service.impl.HelloServiceImpl"/> </beans> ``` 4. 启动Dubbo服务 ```java public class DubboProvider { public static void main(String[] args) throws Exception { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:dubbo-provider.xml"); context.start(); System.in.read(); } } ``` 启动成功后,可以使用Dubbo提供的工具Dubbo Admin来查看已经暴露的服务

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值