Java Netty连接抛出StackOverflowException
最近,我们的生产环境出现问题,导致我们的软件无法正常运行。正如您在下面看到的,我们的代码没有显示在堆栈跟踪中,它只是Netty中的一个递归调用。当这种情况发生时,所有未来的连接都会被拒绝,并且CPU在几个内核上的容量会达到最大。(不是所有的,而是一些),这很奇怪,因为由于所有的网络都断开了,所以不再有流量了,到底在处理什么 如前所述,这只发生在我们的生产环境中。我们发现,当流量增加时,这个问题会在高峰时段更频繁地出现,尽管我们每天24小时运行多个实例,每个实例的连接数为100个,但我们仍然每周只会看到这个问题2次,因此,不幸的是,试图获取有关这个问题的信息是一个痛苦的过程。我们也有理由相信,当我们的服务器之间存在不良连接时,出现此问题的可能性会增加 我以前没有和Netty合作过很多,因为这大部分不是我的代码,我甚至不知道该去哪里找,所以觉得寻求帮助是最好的选择 以下是通道初始化器中的代码:Java Netty连接抛出StackOverflowException,java,netty,Java,Netty,最近,我们的生产环境出现问题,导致我们的软件无法正常运行。正如您在下面看到的,我们的代码没有显示在堆栈跟踪中,它只是Netty中的一个递归调用。当这种情况发生时,所有未来的连接都会被拒绝,并且CPU在几个内核上的容量会达到最大。(不是所有的,而是一些),这很奇怪,因为由于所有的网络都断开了,所以不再有流量了,到底在处理什么 如前所述,这只发生在我们的生产环境中。我们发现,当流量增加时,这个问题会在高峰时段更频繁地出现,尽管我们每天24小时运行多个实例,每个实例的连接数为100个,但我们仍然每周只
@Override
protected void initChannel(SocketChannel ch) {
//Final handler in the pipeline. Deals with the objects once and hands them off to the rest of the code
MessageHandler handler = new MessageHandler(client);
//Converts the raw bytes into objects that we can deal with
CodecsHandler codecs = new CodecsHandler(client, ProtocolType.HANDSHAKE.getProtocol());
//Splits byte streams up into their packets
FramingHandler framing = new FramingHandler();
try {
ch.config().setOption(ChannelOption.IP_TOS, 0x18);
} catch (ChannelException ex) {
log.warn("Kernel lacks support for IP_TOS");
}
ch.config().setAllocator(PooledByteBufAllocator.DEFAULT);
ch.pipeline()
.addLast("idle_timeout", new IdleStateHandler(READ_IDLE_TIMEOUT, WRITE_IDLE_TIMEOUT, 0))
.addLast("framing", framing)
//The Noop handler does nothing (These parts of the pipeline are placed later)
.addLast("compression", NoopHandler.INSTANCE)
.addLast("codecs", codecs)
.addLast("handler", handler);
}
下面是我们的消息处理程序类:
import com.flowpowered.network.Message;
import com.flowpowered.network.session.Session;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import lombok.extern.slf4j.Slf4j;
import mycode.Client;
import java.util.concurrent.atomic.AtomicReference;
@Slf4j
public final class MessageHandler extends SimpleChannelInboundHandler<Message> {
private final AtomicReference<Session> session = new AtomicReference<>(null);
private final Client client;
public MessageHandler(Client client) {
this.client = client;
}
@Override
public void channelActive(ChannelHandlerContext ctx) {
Channel c = ctx.channel();
//Sends some packets packet back to the server and sets the channel object in the client to the channel object above.
//The returned object is an extended class of https://github.com/OverCaste/flow-networking/blob/master/src/main/java/com/flowpowered/networking/session/Session.java
Session s = client.newSession(c);
if (!session.compareAndSet(null, s)) {
throw new IllegalStateException("Session may not be set more than once");
}
s.onReady();
}
@Override
public void channelInactive(ChannelHandlerContext ctx) {
Session session = this.session.get();
if(session != null) {
session.onDisconnect();
} else {
log.warn("Child session was null so could not disconnect");
}
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, Message i) {
//Passes the message off to our session object
session.get().messageReceived(i);
}
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) {
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
session.get().onInboundThrowable(cause);
}
public AtomicReference<Session> getSession() {
return session;
}
}
由于项目的规模,以及不允许我发布所有代码的事实,我剥离了许多不必要的代码并重构了类名
下面是正在发生的事情的一个跟踪,最终似乎
findContextOutbound()
返回了从一开始就调用flush()
的AbstractChannelHandlerContext
的同一个实例,并再次调用flush()
。您可能需要调试并逐步使用这些方法,或者调查使用ChannelHandlerContext
s进行的设置
Cycle:
[repeat from 1] at io.netty.channel.AbstractChannelHandlerContext.flush(AbstractChannelHandlerContext.java:749)
[6] at io.netty.channel.ChannelDuplexHandler.flush(ChannelDuplexHandler.java:117)
[5] at io.netty.channel.AbstractChannelHandlerContext.invokeFlush0(AbstractChannelHandlerContext.java:776)
[4] at io.netty.channel.AbstractChannelHandlerContext.invokeFlush(AbstractChannelHandlerContext.java:768)
[3] at io.netty.channel.AbstractChannelHandlerContext.flush(AbstractChannelHandlerContext.java:749)
[2] at io.netty.channel.AbstractChannelHandlerContext.invokeFlush(AbstractChannelHandlerContext.java:770)
[1] at io.netty.channel.AbstractChannelHandlerContext.flush(AbstractChannelHandlerContext.java:749)
请显示所有处理程序的代码。您还可以在Netty源代码中使用断点进行调试,以跟踪其周期性的原因。您是否尝试过使用大量连接运行测试来重现开发中的问题?我将尝试为处理程序编写代码。至于netty中的一个断点,正如我所提到的,在我们的多个实例的生产中,这种情况每周发生两次。因为它非常罕见,所以调试起来非常困难。我从来没有能够在调试环境中本地重现这个问题。我的大脑完全错过了你问题的第二部分。当我下次有空的时候,我会给它一个机会,并且会报告结果-编辑:这可能需要一段时间来整合工具,让我能够做到这一点,但我会看看我能为此得到多少doneThanks!我们相信我们已经找到了解决方案,但是在我们太快得出任何结论之前,我们将运行更多的测试,并让新代码在生产环境中运行一段时间。如果新代码运行良好,我将写下我们认为的问题。正如我在文章中提到的,由于这些崩溃的发生频率非常低,我们很可能要到下周才能知道它已经修复!令人惊叹的!我知道这并没有直接回答你的问题,但我希望它能帮助你找出潜在的原因。我很好奇它是什么,我期待着更新。我们这周没有任何问题!耶!因此,我们面临的问题似乎是,在使用管道的同时,我们正在使用替换方法修改管道。我刚刚被告知,这是一个坏主意,可能会导致一些奇怪的行为,就像我们看到的那样。但它似乎只是通过改变我们的处理程序来改变协议,而不需要重新构建并插入到管道中,从而解决了我们的问题。
import com.flowpowered.network.util.ByteBufUtils;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageCodec;
import java.util.List;
public final class FramingHandler extends ByteToMessageCodec<ByteBuf> {
private static boolean readableVarInt(ByteBuf buf) {
if (buf.readableBytes() > 5) {
// maximum varint size
return true;
}
int idx = buf.readerIndex();
byte in;
do {
if (buf.readableBytes() < 1) {
buf.readerIndex(idx);
return false;
}
in = buf.readByte();
} while ((in & 0x80) != 0);
buf.readerIndex(idx);
return true;
}
@Override
protected void encode(ChannelHandlerContext ctx, ByteBuf msg, ByteBuf out) {
ByteBufUtils.writeVarInt(out, msg.readableBytes());
out.writeBytes(msg);
}
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
// check for length field readability
in.markReaderIndex();
if (!readableVarInt(in)) {
return;
}
// check for contents readability
int length = ByteBufUtils.readVarInt(in);
if (in.readableBytes() < length) {
in.resetReaderIndex();
return;
}
// read contents into buf
ByteBuf buf = ctx.alloc().buffer(length);
in.readBytes(buf, length);
out.add(buf);
}
}
import com.flowpowered.network.util.ByteBufUtils;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.DecoderException;
import io.netty.handler.codec.EncoderException;
import io.netty.handler.codec.MessageToMessageCodec;
import java.util.List;
import java.util.zip.Deflater;
import java.util.zip.Inflater;
public final class CompressionHandler extends MessageToMessageCodec<ByteBuf, ByteBuf> {
private static final int MAX_INFLATED_BYTES = 1_000_000;
private static final int COMPRESSION_LEVEL = Deflater.DEFAULT_COMPRESSION;
private final int threshold;
private final Inflater inflater;
private final Deflater deflater;
public CompressionHandler(int threshold) {
this.threshold = threshold;
inflater = new Inflater();
deflater = new Deflater(COMPRESSION_LEVEL);
}
@Override
protected void encode(ChannelHandlerContext ctx, ByteBuf msg, List<Object> out) {
ByteBuf prefixBuf = ctx.alloc().buffer(5);
ByteBuf contentsBuf;
try {
if (msg.readableBytes() >= threshold){
// message should be compressed
int index = msg.readerIndex();
int length = msg.readableBytes();
byte[] sourceData = new byte[length];
msg.readBytes(sourceData);
deflater.setInput(sourceData);
deflater.finish();
ByteBuf result = msg.alloc().buffer(length);
byte[] byteArray = new byte[8192];
int totalBytes = 0;
while (!deflater.finished()){
int compressedLength = deflater.deflate(byteArray);
result.writeBytes(byteArray, 0, compressedLength);
totalBytes += compressedLength;
}
deflater.reset();
if (totalBytes == 0){
// compression failed in some weird way
throw new EncoderException("Failed to compress message of size " + length);
} else if (totalBytes >= length){
// compression increased the size. threshold is probably too low
// send as an uncompressed packet
result.release();
ByteBufUtils.writeVarInt(prefixBuf, 0);
msg.readerIndex(index);
msg.retain();
contentsBuf = msg;
} else {
// all is well
ByteBufUtils.writeVarInt(prefixBuf, length);
contentsBuf = result;
}
} else {
// message should be sent through
ByteBufUtils.writeVarInt(prefixBuf, 0);
msg.retain();
contentsBuf = msg;
}
} catch (Exception e){
prefixBuf.release();
throw e;
}
out.add(Unpooled.wrappedBuffer(prefixBuf, contentsBuf));
}
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf msg, List<Object> out) throws Exception {
int index = msg.readerIndex();
int uncompressedSize = ByteBufUtils.readVarInt(msg);
if (uncompressedSize == 0) {
// message is uncompressed
int length = msg.readableBytes();
if (length >= threshold) {
// invalid
throw new DecoderException("Received uncompressed message of size " + length + " greater than threshold " + threshold);
}
msg.retain();
out.add(msg);
} else {
if (uncompressedSize > MAX_INFLATED_BYTES)
//Don't trust this - this is a very big and may come with malicious intent
throw new DecoderException("Resulting uncompressed size is too large for us to handle safely");
// message is compressed
byte[] sourceData = new byte[msg.readableBytes()];
msg.readBytes(sourceData);
inflater.setInput(sourceData);
byte[] destData = new byte[8192];
ByteBuf result = msg.alloc().buffer(uncompressedSize);
int totalBytes = 0;
while (!inflater.finished()){
int resultLength = inflater.inflate(destData);
result.writeBytes(destData, 0, resultLength);
totalBytes += resultLength;
if (totalBytes > uncompressedSize)
throw new DecoderException("Received compressed message claiming to be of size " + uncompressedSize + " but actually larger");
}
inflater.reset();
if (totalBytes == 0) {
// might be a leftover from before compression was enabled (no compression header)
// uncompressedSize is likely to be < threshold
result.release();
msg.readerIndex(index);
msg.retain();
out.add(msg);
} else if (totalBytes != uncompressedSize) {
throw new DecoderException("Received compressed message claiming to be of size " + uncompressedSize + " but actually " + totalBytes);
} else {
out.add(result);
}
}
}
}
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerAdapter;
@ChannelHandler.Sharable
public class NoopHandler extends ChannelHandlerAdapter {
public static final NoopHandler INSTANCE = new NoopHandler();
private NoopHandler() {}
}
java.lang.StackOverflowError: null
at org.apache.logging.slf4j.Log4jLogger.log(Log4jLogger.java:371) ~[log4j-slf4j-impl-2.11.1.jar:2.11.1]
at io.netty.util.internal.logging.LocationAwareSlf4JLogger.log(LocationAwareSlf4JLogger.java:42) ~[netty-common-4.1.31.Final.jar:4.1.31.Final]
at io.netty.util.internal.logging.LocationAwareSlf4JLogger.warn(LocationAwareSlf4JLogger.java:198) ~[netty-common-4.1.31.Final.jar:4.1.31.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeExceptionCaught(AbstractChannelHandlerContext.java:294) ~[netty-transport-4.1.31.Final.jar:4.1.31.Final]
at io.netty.channel.AbstractChannelHandlerContext.notifyHandlerException(AbstractChannelHandlerContext.java:856) ~[netty-transport-4.1.31.Final.jar:4.1.31.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeFlush0(AbstractChannelHandlerContext.java:778) ~[netty-transport-4.1.31.Final.jar:4.1.31.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeFlush(AbstractChannelHandlerContext.java:768) ~[netty-transport-4.1.31.Final.jar:4.1.31.Final]
at io.netty.channel.AbstractChannelHandlerContext.flush(AbstractChannelHandlerContext.java:749) ~[netty-transport-4.1.31.Final.jar:4.1.31.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeFlush(AbstractChannelHandlerContext.java:770) ~[netty-transport-4.1.31.Final.jar:4.1.31.Final]
at io.netty.channel.AbstractChannelHandlerContext.flush(AbstractChannelHandlerContext.java:749) ~[netty-transport-4.1.31.Final.jar:4.1.31.Final]
at io.netty.channel.ChannelDuplexHandler.flush(ChannelDuplexHandler.java:117) ~[netty-transport-4.1.31.Final.jar:4.1.31.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeFlush0(AbstractChannelHandlerContext.java:776) ~[netty-transport-4.1.31.Final.jar:4.1.31.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeFlush(AbstractChannelHandlerContext.java:768) ~[netty-transport-4.1.31.Final.jar:4.1.31.Final]
at io.netty.channel.AbstractChannelHandlerContext.flush(AbstractChannelHandlerContext.java:749) ~[netty-transport-4.1.31.Final.jar:4.1.31.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeFlush(AbstractChannelHandlerContext.java:770) ~[netty-transport-4.1.31.Final.jar:4.1.31.Final]
at io.netty.channel.AbstractChannelHandlerContext.flush(AbstractChannelHandlerContext.java:749) ~[netty-transport-4.1.31.Final.jar:4.1.31.Final]
at io.netty.channel.ChannelDuplexHandler.flush(ChannelDuplexHandler.java:117) ~[netty-transport-4.1.31.Final.jar:4.1.31.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeFlush0(AbstractChannelHandlerContext.java:776) ~[netty-transport-4.1.31.Final.jar:4.1.31.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeFlush(AbstractChannelHandlerContext.java:768) ~[netty-transport-4.1.31.Final.jar:4.1.31.Final]
at io.netty.channel.AbstractChannelHandlerContext.flush(AbstractChannelHandlerContext.java:749) ~[netty-transport-4.1.31.Final.jar:4.1.31.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeFlush(AbstractChannelHandlerContext.java:770) ~[netty-transport-4.1.31.Final.jar:4.1.31.Final]
at io.netty.channel.AbstractChannelHandlerContext.flush(AbstractChannelHandlerContext.java:749) ~[netty-transport-4.1.31.Final.jar:4.1.31.Final]
at io.netty.channel.ChannelDuplexHandler.flush(ChannelDuplexHandler.java:117) ~[netty-transport-4.1.31.Final.jar:4.1.31.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeFlush0(AbstractChannelHandlerContext.java:776) ~[netty-transport-4.1.31.Final.jar:4.1.31.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeFlush(AbstractChannelHandlerContext.java:768) ~[netty-transport-4.1.31.Final.jar:4.1.31.Final]
at io.netty.channel.AbstractChannelHandlerContext.flush(AbstractChannelHandlerContext.java:749) ~[netty-transport-4.1.31.Final.jar:4.1.31.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeFlush(AbstractChannelHandlerContext.java:770) ~[netty-transport-4.1.31.Final.jar:4.1.31.Final]
at io.netty.channel.AbstractChannelHandlerContext.flush(AbstractChannelHandlerContext.java:749) ~[netty-transport-4.1.31.Final.jar:4.1.31.Final]
at io.netty.channel.ChannelDuplexHandler.flush(ChannelDuplexHandler.java:117) ~[netty-transport-4.1.31.Final.jar:4.1.31.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeFlush0(AbstractChannelHandlerContext.java:776) ~[netty-transport-4.1.31.Final.jar:4.1.31.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeFlush(AbstractChannelHandlerContext.java:768) ~[netty-transport-4.1.31.Final.jar:4.1.31.Final]
at io.netty.channel.AbstractChannelHandlerContext.flush(AbstractChannelHandlerContext.java:749) ~[netty-transport-4.1.31.Final.jar:4.1.31.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeFlush(AbstractChannelHandlerContext.java:770) ~[netty-transport-4.1.31.Final.jar:4.1.31.Final]
at io.netty.channel.AbstractChannelHandlerContext.flush(AbstractChannelHandlerContext.java:749) ~[netty-transport-4.1.31.Final.jar:4.1.31.Final]
at io.netty.channel.ChannelDuplexHandler.flush(ChannelDuplexHandler.java:117) ~[netty-transport-4.1.31.Final.jar:4.1.31.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeFlush0(AbstractChannelHandlerContext.java:776) ~[netty-transport-4.1.31.Final.jar:4.1.31.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeFlush(AbstractChannelHandlerContext.java:768) ~[netty-transport-4.1.31.Final.jar:4.1.31.Final]
at io.netty.channel.AbstractChannelHandlerContext.flush(AbstractChannelHandlerContext.java:749) ~[netty-transport-4.1.31.Final.jar:4.1.31.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeFlush(AbstractChannelHandlerContext.java:770) ~[netty-transport-4.1.31.Final.jar:4.1.31.Final]
at io.netty.channel.AbstractChannelHandlerContext.flush(AbstractChannelHandlerContext.java:749) ~[netty-transport-4.1.31.Final.jar:4.1.31.Final]
at io.netty.channel.ChannelDuplexHandler.flush(ChannelDuplexHandler.java:117) ~[netty-transport-4.1.31.Final.jar:4.1.31.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeFlush0(AbstractChannelHandlerContext.java:776) ~[netty-transport-4.1.31.Final.jar:4.1.31.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeFlush(AbstractChannelHandlerContext.java:768) ~[netty-transport-4.1.31.Final.jar:4.1.31.Final]
at io.netty.channel.AbstractChannelHandlerContext.flush(AbstractChannelHandlerContext.java:749) ~[netty-transport-4.1.31.Final.jar:4.1.31.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeFlush(AbstractChannelHandlerContext.java:770) ~[netty-transport-4.1.31.Final.jar:4.1.31.Final]
at io.netty.channel.AbstractChannelHandlerContext.flush(AbstractChannelHandlerContext.java:749) ~[netty-transport-4.1.31.Final.jar:4.1.31.Final]
at io.netty.channel.ChannelDuplexHandler.flush(ChannelDuplexHandler.java:117) ~[netty-transport-4.1.31.Final.jar:4.1.31.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeFlush0(AbstractChannelHandlerContext.java:776) ~[netty-transport-4.1.31.Final.jar:4.1.31.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeFlush(AbstractChannelHandlerContext.java:768) ~[netty-transport-4.1.31.Final.jar:4.1.31.Final]
at io.netty.channel.AbstractChannelHandlerContext.flush(AbstractChannelHandlerContext.java:749) ~[netty-transport-4.1.31.Final.jar:4.1.31.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeFlush(AbstractChannelHandlerContext.java:770) ~[netty-transport-4.1.31.Final.jar:4.1.31.Final]
at io.netty.channel.AbstractChannelHandlerContext.flush(AbstractChannelHandlerContext.java:749) ~[netty-transport-4.1.31.Final.jar:4.1.31.Final]
at io.netty.channel.ChannelDuplexHandler.flush(ChannelDuplexHandler.java:117) ~[netty-transport-4.1.31.Final.jar:4.1.31.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeFlush0(AbstractChannelHandlerContext.java:776) ~[netty-transport-4.1.31.Final.jar:4.1.31.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeFlush(AbstractChannelHandlerContext.java:768) ~[netty-transport-4.1.31.Final.jar:4.1.31.Final]
at io.netty.channel.AbstractChannelHandlerContext.flush(AbstractChannelHandlerContext.java:749) ~[netty-transport-4.1.31.Final.jar:4.1.31.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeFlush(AbstractChannelHandlerContext.java:770) ~[netty-transport-4.1.31.Final.jar:4.1.31.Final]
at io.netty.channel.AbstractChannelHandlerContext.flush(AbstractChannelHandlerContext.java:749) ~[netty-transport-4.1.31.Final.jar:4.1.31.Final]
at io.netty.channel.ChannelDuplexHandler.flush(ChannelDuplexHandler.java:117) ~[netty-transport-4.1.31.Final.jar:4.1.31.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeFlush0(AbstractChannelHandlerContext.java:776) ~[netty-transport-4.1.31.Final.jar:4.1.31.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeFlush(AbstractChannelHandlerContext.java:768) ~[netty-transport-4.1.31.Final.jar:4.1.31.Final]
at io.netty.channel.AbstractChannelHandlerContext.flush(AbstractChannelHandlerContext.java:749) ~[netty-transport-4.1.31.Final.jar:4.1.31.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeFlush(AbstractChannelHandlerContext.java:770) ~[netty-transport-4.1.31.Final.jar:4.1.31.Final]
at io.netty.channel.AbstractChannelHandlerContext.flush(AbstractChannelHandlerContext.java:749) ~[netty-transport-4.1.31.Final.jar:4.1.31.Final]
at io.netty.channel.ChannelDuplexHandler.flush(ChannelDuplexHandler.java:117) ~[netty-transport-4.1.31.Final.jar:4.1.31.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeFlush0(AbstractChannelHandlerContext.java:776) ~[netty-transport-4.1.31.Final.jar:4.1.31.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeFlush(AbstractChannelHandlerContext.java:768) ~[netty-transport-4.1.31.Final.jar:4.1.31.Final]
at io.netty.channel.AbstractChannelHandlerContext.flush(AbstractChannelHandlerContext.java:749) ~[netty-transport-4.1.31.Final.jar:4.1.31.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeFlush(AbstractChannelHandlerContext.java:770) ~[netty-transport-4.1.31.Final.jar:4.1.31.Final]
at io.netty.channel.AbstractChannelHandlerContext.flush(AbstractChannelHandlerContext.java:749) ~[netty-transport-4.1.31.Final.jar:4.1.31.Final]
at io.netty.channel.ChannelDuplexHandler.flush(ChannelDuplexHandler.java:117) ~[netty-transport-4.1.31.Final.jar:4.1.31.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeFlush0(AbstractChannelHandlerContext.java:776) ~[netty-transport-4.1.31.Final.jar:4.1.31.Final]
... ... ...
Cycle:
[repeat from 1] at io.netty.channel.AbstractChannelHandlerContext.flush(AbstractChannelHandlerContext.java:749)
[6] at io.netty.channel.ChannelDuplexHandler.flush(ChannelDuplexHandler.java:117)
[5] at io.netty.channel.AbstractChannelHandlerContext.invokeFlush0(AbstractChannelHandlerContext.java:776)
[4] at io.netty.channel.AbstractChannelHandlerContext.invokeFlush(AbstractChannelHandlerContext.java:768)
[3] at io.netty.channel.AbstractChannelHandlerContext.flush(AbstractChannelHandlerContext.java:749)
[2] at io.netty.channel.AbstractChannelHandlerContext.invokeFlush(AbstractChannelHandlerContext.java:770)
[1] at io.netty.channel.AbstractChannelHandlerContext.flush(AbstractChannelHandlerContext.java:749)
[1] AbstractChannelHandlerContext.flush():
final AbstractChannelHandlerContext next = findContextOutbound(); // see following method
EventExecutor executor = next.executor();
if (executor.inEventLoop()){
next.invokeFlush() // -> [2]
} else { /* threadded invokeFlush */ }
// do you happen to have two AbstractChannelHandlerContexts ctx1 and ctx2, such that?:
// ctx1.prev == ctx2 && ctx2.prev == ctx1;
AbstractChannelHandlerContext.findContextOutbound():
AbstractChannelHandlerContext ctx = this;
do {
ctx = ctx.prev;
} while (!ctx.outbound);
return ctx;
[2] AbstractChannelHandlerContext.invokeFlush():
// this time invokeHandler() returns `false`
if (invokeHandler()){
invokeFlush0();
} else {
flush(); // -> [3]
}
/**
* Makes best possible effort to detect if `ChannelHandler.handlerAdded(ChannelHandlerContext)`
* was called yet. If not return `false` and if called or could not detect return `true`.
*
* If this method returns `false` we will not invoke the `ChannelHandler` but just forward the event.
* This is needed as `DefaultChannelPipeline` may already put the `ChannelHandler` in the linked-list
* but not called `ChannelHandler.handlerAdded(ChannelHandlerContext)`.
*/
AbstractChannelHandlerContext.invokeHandler():
int handlerState = this.handlerState;
return handlerState == ADD_COMPLETE || (!ordered && handlerState == ADD_PENDING);
[3] AbstractChannelHandlerContext.flush():
final AbstractChannelHandlerContext next = findContextOutbound();
EventExecutor executor = next.executor();
if (executor.inEventLoop())
next.invokeFlush() // -> [4]
} else { /* threadded invokeFlush */ }
[4] AbstractChannelHandlerContext.invokeFlush():
// this time invokeHandler() returns `true`
if (invokeHandler()){
invokeFlush0(); // -> [5]
} else {
flush();
}
[5] AbstractChannelHandlerContext.invokeFlush0():
try {
((ChannelOutboundHandler) handler()).flush(this); // -> [6]
} catch (Throwable t) {
notifyHandlerException(t); // ultimately reaches this when `StackOverflowException`
}
// ChannelDuplexHandler implements ChannelOutboundHandler
[6] ChannelDuplexHandler.flush(ChannelHandlerContext ctx):
// ctx is the original `AbstractChannelHandlerContext` and the cycle repeats
ctx.flush(); // -> repeat from [1]