io.netty.util.concurrent.BlockingOperationException: DefaultChannelPromise 错误笔记

背景信息

按常规部署好服务,启动没啥问题,运行的时候出现该错误。  io.netty.util.concurrent.BlockingOperationException: DefaultChannelPromise。 

由于版本包已经用过很多次了,之前从未出现过该问题,但是问题现在就是出现了。既然出现,那么研发就得解决,见没见过不重要,先分析。

问题出现做了哪些工作?

1. 检查版本包是否一致,近期有无修改;

2. 检查安装环境信息,jdk版本,虚拟机情况,内存,性能条件;

结果分析

 问题一很容易确认,近期版本没有变化,与正常环境的包一致;

那么问题很可能就是环境问题,经检查jdk版本均为8,用户权限及通讯端口交互正常,是在处理大报文的时候,服务端要去客户端采集日志展示超时,从日志看,服务端确实等待了配置的时候,最终没有拿到结果,提示超时错误。 继续分析客户端的情况,发现服务调用的时候,日志打印很慢,由于处理的结果集内容较多,确实需要较长的时间处理请求,但是慢归慢,内容确实也拿到了,在给服务端返回的时候有错误日志。是在netty推送报文的时候,出了异常,核心错误如下:

io.netty.util.concurrent.BlockingOperationException: DefaultChannelPromise
at io.netty.util.concurrent.DefaultPromise.checkDeadLock(DefaultPromise.java:391)
    at io.netty.channel.DefaultChannelPromise.checkDeadLock(DefaultChannelPromise.java:157)
    at io.netty.util.concurrent.DefaultPromise.await(DefaultPromise.java:252)
    at io.netty.channel.DefaultChannelPromise.await(DefaultChannelPromise.java:129)
    at io.netty.channel.DefaultChannelPromise.await(DefaultChannelPromise.java:28)
    at io.netty.util.concurrent.DefaultPromise.sync(DefaultPromise.java:219)
    at io.netty.channel.DefaultChannelPromise.sync(DefaultChannelPromise.java:117)
    at io.netty.channel.DefaultChannelPromise.sync(DefaultChannelPromise.java:28)

前后还有其他错误,在调用了netty的writeAndFlush方法,之后还调用了syncUninterruptibly();

ctx.channel().writeAndFlush(serverResponseMsg).syncUninterruptibly();

由于此次部署服务器版本较低,且性能一般(实际比常用的差很多),重点怀疑性能方面,从错误日志看,确实像是死锁了,查阅资料(过程不表),大体都是说 writeAndFlush()方法分为两步, 先 write 再 flush,write方法实际上并不是真的将消息写出去, 而是将消息和此次操作的promise放入到了一个队列中,flush才是将数据写出去的地方,即write方法只是把发送数据放入一个缓存,而不会真实的发送,而flush则是将放入缓存的数据发送出去。

syncUninterruptibly()方法:让当前线程同步等待消息发送完成,且无中断异常。

(Netty权威指南和Netty实战中都有谈到,不要在ChannelHandler方法中调用sync()或await()方法,会有可能引起死锁。)

既然知道原因,那么修改就不要调用sync()方法,根据官方建议是要添加一个cf.addListener(new ChannelFutureListener(),参考如下写法:

private void pushMessage(T message) {
    try {
        ChannelFuture cf = cxt.writeAndFlush(message);
        cf.addListener(new ChannelFutureListener() {
            @Override
            public void operationComplete(ChannelFuture future) throws PushException {
                if (future.isSuccess()) {
                    logger.debug("send success.");
                } else {
                    throw new PushException("Failed to send message.");
                }
                Throwable cause = future.cause();
                if (cause != null) {
                    throw new PushException(cause);
                }
            }
        });
    } catch (LostConnectException e) {
        this.fireError(new PushException(e));
    } catch (Exception e) {
        this.fireError(new PushException(e));
    } catch (Throwable e) {
        this.fireError(new PushException(e));
    }
}

参考资料

https://www.cnblogs.com/Guoyutian/p/5121780.html

https://blog.csdn.net/yzh_1346983557/article/details/88300156


版权声明:本文为weixin_39734304原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。