背景信息
按常规部署好服务,启动没啥问题,运行的时候出现该错误。 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.youkuaiyun.com/yzh_1346983557/article/details/88300156