我找到了一个非常简单的解决方案,允许入站和出站异常到达与管道中最后一个 ChannelHandler 相同的异常处理程序。
我的管道设置如下:
//Inbound propagation
socketChannel.pipeline()
.addLast(new Decoder())
.addLast(new ExceptionHandler());
//Outbound propagation
socketChannel.pipeline()
.addFirst(new OutboundExceptionRouter())
.addFirst(new Encoder());
这是我的 ExceptionHandler 的内容,它记录捕获的异常:
public class ExceptionHandler extends ChannelInboundHandlerAdapter {
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
log.error("Exception caught on channel", cause);
}
}
现在,OutBoundExceptionRouter 中甚至允许 ExceptionHandler 处理出站异常:
public class OutboundExceptionRouter extends ChannelOutboundHandlerAdapter {
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
promise.addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE);
super.write(ctx, msg, promise);
}
}
这是我的管道中调用的第一个出站处理程序,它的作用是将一个侦听器添加到将执行的出站写入承诺中future.channel().pipeline().fireExceptionCaught(future.cause());
当承诺落空时。这fireExceptionCaught
方法通过管道向入站方向传播异常,最终到达 ExceptionHandler。
如果有人感兴趣的话,从 Netty 4.1 开始,我们需要添加一个监听器来获取异常,因为在对通道执行 writeAndFlush 后,调用Write0方法 https://github.com/netty/netty/blob/830fc0d660896ce781aba0a8eb614053a4fe9201/transport/src/main/java/io/netty/channel/AbstractChannelHandlerContext.java#L715在 AbstractChannelHandlerContext.java 中调用,它将写入操作包装在 try catch 块中。 catch 块通知 Promise 而不是调用fireExceptionCaught
就像 invokeChannelRead 方法一样 https://github.com/netty/netty/blob/830fc0d660896ce781aba0a8eb614053a4fe9201/transport/src/main/java/io/netty/channel/AbstractChannelHandlerContext.java#L376用于入站消息。