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

博客内容描述了一次在部署服务时遇到的io.netty.util.concurrent.BlockingOperationException问题,经过排查确认不是版本或环境配置导致,而是由于处理大报文时服务端等待客户端日志超时,进而引发客户端Netty推送报文异常。分析发现可能是由于syncUninterruptibly()方法导致的死锁。解决方案是遵循Netty官方建议,避免在ChannelHandler中使用sync(),改为使用ChannelFuture监听器来确保消息发送完成。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

背景信息

按常规部署好服务,启动没啥问题,运行的时候出现该错误。  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

C:\Windows\system32\cmd.e:x + zation(InitDestroyAnnotationBeanPostProcessor.java:136) ...26 common frames omittedCaused by: java.net.BindException: Address already in use: bindsun.nio.ch.Net.bind(Native Method)atatsun.nio.ch.Net.bind(Net.java:433)at sun.nio.ch.Net.bind(Net.java:425)sun.nio.ch.ServerSocketChannelImpl.bind(ServerSocketChannelImpl.java:223)atat io .grpc.netty.shaded. io.netty.channel.socket.nio.NioServerSocketchannel.doBind(NioServerSocketchannel.java:13at io . grpc.netty.shaded.io.netty.channel.Abstractchannel$AbstractUnsafe.bind(Abstractchannel.java:551)at io .grpc.netty.shaded. io.netty.channel.DefaultchannelPipeline$HeadContext.bind(DefaultchannelPipeline.java:134at io .grpc.netty.shaded.io.netty.channel.AbstractchannelHandlerContext.invokeBind(AbstractChannelHandlerContextat io.grpc.netty.shaded.io.netty.channel.AbstractchannelHandlerContext.bind(AbstractchannelHandlerContext.java:4at io.grpc .netty.shaded.io.netty.channel.Defaultchannelpipeline.bind(DefaultchannelPipeline.java:985)atio.grpc.nettyshaded.io.netty.channel.AbstractChannel.bind(Abstractchannel.java:247)atio.grpc.netty.shaded.io.netty..bootstrap.AbstractBootstrap$2.run(AbstractBootstrap.java:344)at io.grpc.netty.shaded, io.netty.util,concurrent.AbstractEventExecutor,safeExecute(AbstractEventExecutor.java:16 2) 0 java:503) 88) at io .grpc.netty,shaded. io.netty.util.concurrent .singleThreadEventExecutor.runAllTasks(SingleThreadEventExecutorio.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:518)io.grpc.netty..shaded.at io.grpc.netty.shaded. io.netty.util.concurrent ,singleThreadEventExecutor$6.run(SingleThreadEventExecutor .javaat io.grpc.netty.shaded.io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)at io.grpc.netty.shaded. io.netty.util.concurrent .FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)at java.lang.Thread.run(Thread.iava:748) at java:510) 1044)
最新发布
03-11
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值