apache / apache/dubbo

过期的Invoker 连接 持续在 时间轮线程中执行重连任务,抛出大量异常

Open
#11,289 23 comments 0 reactions 0 assignees View on GitHub
status/waiting-for-feedback
Dominant language
Java
Stars
41.6k
Forks
26.4k
Avg merge
15h 13m
Merged PRs (30d)
4

Description

异常栈如下:
```
at org.apache.dubbo.remoting.transport.netty4.NettyClient.doConnect(NettyClient.java:238)
at org.apache.dubbo.remoting.transport.netty4.NettyClient.doConnect(NettyClient.java:171)
at org.apache.dubbo.remoting.transport.AbstractClient.connect(AbstractClient.java:220)
at org.apache.dubbo.remoting.transport.AbstractClient.reconnect(AbstractClient.java:274)
at org.apache.dubbo.remoting.exchange.support.header.HeaderExchangeClient.reconnect(HeaderExchangeClient.java:171)
at org.apache.dubbo.remoting.exchange.support.header.ReconnectTimerTask.doTask(ReconnectTimerTask.java:52)
at org.apache.dubbo.remoting.exchange.support.header.AbstractTimerTask.run(AbstractTimerTask.java:87)
at org.apache.dubbo.common.timer.HashedWheelTimer$HashedWheelTimeout.expire(HashedWheelTimer.java:653)
at org.apache.dubbo.common.timer.HashedWheelTimer$HashedWheelBucket.expireTimeouts(HashedWheelTimer.java:732)
at org.apache.dubbo.common.timer.HashedWheelTimer$Worker.run(HashedWheelTimer.java:454)
at java.lang.Thread.run(Thread.java:748)
```

可以确定的是 我们注册中心(Nacos)已经 通知 Consumer刷新Invoker列表了。
_查阅代码 ServiceDiscoveryRegistryDirectory.refreshInvoker 方法已经触发。_
接着触发了 ServiceDiscoveryRegistryDirectory.destroyUnusedInvokers 方法
接着触发了 DubboInvoker.destroyUnusedInvokers.destroyInternal 方法
接着触发了 HeaderExchangeClient.doClose 方法
但是在 ` private void doClose() {
if (heartBeatTimer != null) {
heartBeatTimer.cancel();
heartBeatTimer = null;
}
if (reconnectTimer != null) {
reconnectTimer.cancel();
reconnectTimer = null;
}
}`
_使用的是 reconnectTimer.cancel(); reconnectTimer 是一个 Timeout 。_
而在 ReconnectTimerTask 的父类 AbstractTimerTask 中会进行reput
` public void run(Timeout timeout) throws Exception {
Collection c = channelProvider.getChannels();
for (Channel channel : c) {
if (channel.isClosed()) {
continue;
}
doTask(channel);
}
reput(timeout, tick);
}`
Reput 中重新将 task放入 时间轮,生成一个新的 timeout

` private void reput(Timeout timeout, Long tick) {
if (timeout == null || tick == null) {
throw new IllegalArgumentException();
}
if (cancel) {
return;
}
Timer timer = timeout.timer();
if (timer.isStop() || timeout.isCancelled()) {
return;
}
timer.newTimeout(timeout.task(), tick, TimeUnit.MILLISECONDS);
}`

reput 中 重新生成了一个 新的timout 如上,因此 这个 时间轮重试任务将一直执行。

HeaderExchangeClient中关闭的是一个旧的 Timeout,最终导致 异常一直打印。

于是想请问一下 HeaderExchangeClient 的 doClose方法中 是否应该使用 heartBeatTimerTask.cancel()

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.