arthas tunnel client may throw BlockingOperationException, then reconnect fail problem

Created on 1 Jul 2020  ·  4Comments  ·  Source: alibaba/arthas

  • [x] 我已经在 issues 里搜索,没有重复的issue。

环境信息

  • Arthas 版本: master
  • 操作系统版本: macOS 10.15.5
  • 目标进程的JVM版本: 1.8.0_231

重现问题的步骤

  1. 问题截图

图片是在钉钉的[Arthas开源交流群2]群里别人发出来的, 我看到之后就去查看了源码,自己模拟了一下,发现代码的确存在问题.

图片

  1. 根据图片上的调用堆栈信息,定位到代码是从com.alibaba.arthas.tunnel.client.TunnelClient开始。
    分析代码流程如下

2.1 在连接服务端时,客户端A使用了EventLoopGroup.

2.2 com.alibaba.arthas.tunnel.client.TunnelClientSocketClientHandler类在处理读请求时,启动了com.alibaba.arthas.tunnel.client.ForwardClient类. ForwardClient类中也创建了一个客户端B, 而客户端B使用的EventLoopGroup与客户端A使用的EventLoopGroup是同一个.

2.3 客户端B在调用b.connect(tunnelServerURI.getHost(), port).sync().channel()时候调用了sync()方法. 继续调用await(),
调用checkDeadLock()

2.4 每次服务端给客户端A发送数据之后,客户端A在读取数据过程都会创建新的客户端. 由于所有的客户端使用的是同一个EventLoopGroup, 虽然Netty底层是通过轮询的方式,每次给客户端从EventLoopGroup分配一个EventLoop. 假如EventLoopGroup大小=8,当服务端发送第九次数据给客户端A的时候,这个时候已经存在9个客户端了,那么第9个客户端和第1个
客户端使用的就是同一个EventLoop, 这样在调用checkDeadLock()时候就会报错.
其实虽然两个客户端使用了同一个EventLoop,也不见得一定报错,但是源码中使用channel = b.connect(tunnelServerURI.getHost(), port).sync().channel()这样的书写方式就会报错.改成类似这样的写法就可以了

ChannelFuture channelFuture = b.connect("127.0.0.1", 8080);
        channelFuture.addListener(new ChannelFutureListener() {
            @Override
            public void operationComplete(ChannelFuture channelFuture) throws Exception {
                Channel channel = channelFuture.sync().channel();

            }
        });

期望的结果

可以优化成使用channelFuture.addListener方式

bug

Most helpful comment

已经修复了,欢迎测试验证。

All 4 comments

分析非常清楚,欢迎提交PR来修复。

@infuq 感谢!昨天请教后,我修改了 ForwardClient 的 group 参数 ,重新测试了一下,没有再发现deadlock问题了,改动如下:

ForwardClient forwardClient = new ForwardClient(forwardUri,
                        new URI(this.tunnelClient.getLocalServerUrl()), new NioEventLoopGroup());
forwardClient.start();

但是今天的测试依然存在问题,页面刷新几次,还是有一定概率连接不上控制台了。定位了一下卡在了ForwardClientSocketClientHandler#connectLocalServer ,而这个位置正常情况不会卡住。

handshakeFuture.sync();

image

@infuq 你好,再补充一点,tunnel server 也有这个问题,你提 PR 的时候注意一下,之前的异常栈是 tunnel client 的,server 端异常栈如下


io.netty.util.concurrent.BlockingOperationException: DefaultPromise@4f3ffb6f(incomplete)
    at io.netty.util.concurrent.DefaultPromise.checkDeadLock(DefaultPromise.java:461) [netty-common-4.1.45.Final.jar:4.1.45.Final]
    at io.netty.util.concurrent.DefaultPromise.await0(DefaultPromise.java:666) [netty-common-4.1.45.Final.jar:4.1.45.Final]
    at io.netty.util.concurrent.DefaultPromise.awaitUninterruptibly(DefaultPromise.java:304) [netty-common-4.1.45.Final.jar:4.1.45.Final]
    at com.alibaba.arthas.tunnel.server.TunnelSocketFrameHandler.connectArthas(TunnelSocketFrameHandler.java:147) ~[classes/:na]
    at com.alibaba.arthas.tunnel.server.TunnelSocketFrameHandler.userEventTriggered(TunnelSocketFrameHandler.java:59) ~[classes/:na]
    at io.netty.channel.AbstractChannelHandlerContext.invokeUserEventTriggered(AbstractChannelHandlerContext.java:344) [netty-transport-4.1.45.Final.jar:4.1.45.Final]
    at io.netty.channel.AbstractChannelHandlerContext.invokeUserEventTriggered(AbstractChannelHandlerContext.java:330) [netty-transport-4.1.45.Final.jar:4.1.45.Final]
    at io.netty.channel.AbstractChannelHandlerContext.fireUserEventTriggered(AbstractChannelHandlerContext.java:322) [netty-transport-4.1.45.Final.jar:4.1.45.Final]

已经修复了,欢迎测试验证。

Was this page helpful?
0 / 5 - 0 ratings