【问题标题】:How to catch all exception in netty如何在netty中捕获所有异常
【发布时间】:2022-02-02 09:45:28
【问题描述】:

据我所知,netty 通过覆盖方法 exceptionCaught() 处理异常。但我想要的是一个可以处理所有异常进出的处理程序。所以,管道应该是这样的:

InboundExceptionHandler - inboundHandler1 - inboundHandler2 - outboundHandler1 - outboundHandler2 - OutboundExceptionHandler

这意味着我应该在我的管道中放置 2 个异常处理程序,头尾分开。但我觉得它看起来很丑。有更好的主意吗?

【问题讨论】:

    标签: java exception-handling netty


    【解决方案1】:

    您可以在管道的顶部/尾部仅使用一个入站和出站异常处理程序。如果你想捕获所有异常,你可以这样做(我假设这是 Netty 4.0):

    import io.netty.channel.*;
    
    import java.net.SocketAddress;
    
    public class ExceptionHandler extends ChannelDuplexHandler {
        
        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
            // Uncaught exceptions from inbound handlers will propagate up to this handler 
        }
    
        @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()) {
                        // Handle connect exception here...
                        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()) {
                        // Handle write exception here...
                        Throwable failureCause = future.cause();
                    }
                }
            }));
        }
        
        // ... override more outbound methods to handle their exceptions as well
    }
    

    入站处理程序引发的任何异常都将“向上”传播管道并调用此处理程序的 exceptionCaught() 方法,假设下面的处理程序不使用它们。

    对于像write()connect() 这样的出站操作,您需要添加一个ChannelFutureListener 来捕获它们的异常。 exceptionCaught() 方法仅针对来自入站事件的异常调用,例如 channelRead()channelActive() 等。

    在管道的“顶部”使用这个处理程序,我们可以从下面的所有出站处理程序中捕获异常。假设您的一个出站处理程序正在执行一些编码,但由于异常而失败,这将由我们添加到 write() 操作承诺的通道未来侦听器处理。

    如果这个异常处理程序像你最初建议的那样安装在管道的“底部”/头部,那么它不会看到来自它上面的处理程序的异常,因为如果写入失败,它的 write() 方法将永远不会被调用以前的处理程序。这就是为什么这个处理程序必须在顶部。

    为了避免混淆管道的顶部/底部,我将如何配置您的示例管道:

    pipeline.addLast(outboundHandler2)        // bottom
            .addLast(outboundHandler1)
            .addLast(inboundHandler2)
            .addLast(inboundHandler1)
            .addLast(new ExceptionHandler()); // top
    

    【讨论】:

    • 这不能与 voidPromise 在写入时或以最小化不必要的对象创建的方式一起使用。你能详细说明一下优化版本吗
    • 如果您使用 voidPromise 进行写入,那么您不能调用 addListener(),但是任何出站写入异常都会传播到 exceptionCaught(),因此它们仍然可以在那里处理。在 4.1 上,您可以调用 promise.isVoid() 来处理任何一种情况。为了尽量减少对象创建,我上面使用的 ChannelFutureListener 匿名类可以声明为实例成员,因此每个处理程序只创建一次。
    • 嗨,本,感谢您的回答。我了解入站部分,但对出站部分感到困惑。假设有一条传入消息通过管道传输。 inboundHandler1 收到消息,然后通过调用 ctx.write() 进行响应。既然ExceptionHandler 从来没有接触过这个消息,那么如何调用它的write() 方法来设置promise 回调呢?我想我一定是错过了什么……
    • 你是对的,如果在处理入站消息时从另一个处理程序调用ctx.write()ExceptionHandler 将不知道它。您有几个选择: - 创建一个 ChannelPromise (ctx.newPromise()) 并向其添加您自己的侦听器,以处理当前 ChannelHandler 中的异常 - 使用 ctx.write(message, ctx.voidPromise()) - 如果使用 void Promise 是安全的 - 那么任何写入异常都会传播直到 ExceptionHandlerexceptionCaught 方法。
    【解决方案2】:

    最终的解决方案是自定义 ChannelInitializer 甚至可以添加更多逻辑

    【讨论】:

    • 您的答案可以通过额外的支持信息得到改进。请edit 添加更多详细信息,例如引用或文档,以便其他人可以确认您的答案是正确的。你可以找到更多关于如何写好答案的信息in the help center
    猜你喜欢
    • 1970-01-01
    • 2014-09-18
    • 2023-03-16
    • 1970-01-01
    • 2018-07-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-03
    相关资源
    最近更新 更多