你可以在你的管道的顶部/尾部只设置单个的入站和出站异常处理程序。如果你想要捕获所有异常,你可以采取以下措施(我假设这是Netty 4.0):
import io.netty.channel.*;
import java.net.SocketAddress;
public class ExceptionHandler extends ChannelDuplexHandler {
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
}
@Override
public void connect(ChannelHandlerContext ctx, SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) {
ctx.connect(remoteAddress, localAddress, promise.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) {
if (!future.isSuccess()) {
Throwable failureCause = future.cause();
}
}
}));
}
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) {
ctx.write(msg, promise.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) {
if (!future.isSuccess()) {
Throwable failureCause = future.cause();
}
}
}));
}
}
所有由入站处理程序引发的异常都将向上传播并调用此处理程序的 exceptionCaught()
方法,假设下面没有处理程序来消耗它们。
对于像 write()
和 connect()
这样的出站操作,您需要添加一个 ChannelFutureListener
来捕获它们的异常。 exceptionCaught()
方法仅针对入站事件(例如 channelRead()
、channelActive()
等)中的异常被调用。
这个处理程序位于管道的顶部,我们可以捕获下面所有出站处理程序的异常。假设您的某个出站处理程序正在进行编码,但由于发生异常导致失败,那么这将由我们添加到 write()
操作承诺中的通道未来监听器处理。
如果将此异常处理程序安装在管道的“底部”或头部,就如您最初建议的那样,那么它将无法看到来自其上方处理程序的异常,因为如果写入在前一个处理程序失败,那么它的 write()
方法将永远不会被调用。这就是为什么必须将此处理程序放置在顶部的原因。
为了避免关于管道顶部/底部的混淆,这是我如何配置您的示例管道:
pipeline.addLast(outboundHandler2) // bottom
.addLast(outboundHandler1)
.addLast(inboundHandler2)
.addLast(inboundHandler1)
.addLast(new ExceptionHandler()); // top
inboundHandler1
接收到消息后,通过调用ctx.write()
进行响应。由于ExceptionHandler
从未触及此消息,那么它的write()
方法将如何被调用以设置承诺回调?我想我可能漏掉了什么... - Dmitry Minkovskyctx.write()
,ExceptionHandler
将不会知道它。你有几个选项:ctx.newPromise()
) 并向其中添加自己的监听器,以处理当前 ChannelHandler 中的异常。ctx.write(message, ctx.voidPromise())
- 如果可以使用 void promises 安全地使用 - 那么任何写入异常都将传播到ExceptionHandler
的exceptionCaught
方法中。