关于Netty的BlockingOperationException问题
程序员文章站
2022-04-22 18:03:37
...
记录遇到的一个死锁异常。出现的场景是在客户端断线重连时。
重连时代码类似这样:
private void reconnect() {
if (bootstrap == null)
throw new IllegalArgumentException("bootstrap cannot be null");
//如果已经连接,则直接【连接成功】
if (channel == null || !channel.isActive()) {
//连接
channel = bootstrap.connect().awaitUninterruptibly().channel();
}
}
抛出异常是这样:
io.netty.util.concurrent.BlockingOperationException: DefaultChannelPromise@40edf502(incomplete)
at io.netty.util.concurrent.DefaultPromise.checkDeadLock(DefaultPromise.java:386)
at io.netty.channel.DefaultChannelPromise.checkDeadLock(DefaultChannelPromise.java:159)
at io.netty.util.concurrent.DefaultPromise.awaitUninterruptibly(DefaultPromise.java:236)
at io.netty.channel.DefaultChannelPromise.awaitUninterruptibly(DefaultChannelPromise.java:137)
at io.netty.channel.DefaultChannelPromise.awaitUninterruptibly(DefaultChannelPromise.java:30)
自然就去看源码:
//DefaultPromise.java
public Promise<V> awaitUninterruptibly() {
if (this.isDone()) {
return this;
} else {
this.checkDeadLock();
boolean interrupted = false;
synchronized(this) {
while(!this.isDone()) {
this.incWaiters();
try {
this.wait();
} catch (InterruptedException var9) {
interrupted = true;
} finally {
this.decWaiters();
}
}
}
if (interrupted) {
Thread.currentThread().interrupt();
}
return this;
}
}
//DefaultPromise.java
protected void checkDeadLock() {
EventExecutor e = this.executor();
if (e != null && e.inEventLoop()) {
throw new BlockingOperationException(this.toString());
}
}
//AbstractEventExecutor.java
public boolean inEventLoop() {
return this.inEventLoop(Thread.currentThread());
}
//SingleThreadEventExecutor.java
public boolean inEventLoop(Thread thread) {
return thread == this.thread;
}
发现这是在判断当前线程(抛出异常的线程)是否就是运行bootstrap的这条线程,如果是,那么就抛出异常。
…
同一条线程自然不能同时await与notify,所以抛出异常很正常。
调试了一会,发现只有在比较老旧的机子上运行才会出现这种问题…于是怀疑方向转向性能,或者说转向线程数量。
bootstrap.group(new NioEventLoopGroup())
//MultithreadEventLoopGroup.java
private static final int DEFAULT_EVENT_LOOP_THREADS = Math.max(1, SystemPropertyUtil.getInt("io.netty.eventLoopThreads", NettyRuntime.availableProcessors() * 2));
看到这里,我使用了
bootstrap.group(new NioEventLoopGroup(1))
发现重连会百分百死锁,于是这个锅就得扣到硬件的性能头上了。
起因是await操作,任何await操作都有BlockingOperationException风险,比如一些大佬所说的只使用ctx.write而不flush之类的问题,其实内部也是因为await。然后才是线程数量,如果可能,尽量手动给定线程数。
上一篇: 19.hibernate多对多双向关联
下一篇: 使用netty发送报文的坑