Easy Netty 系列(六):异常处理详解

#Netty [字体 ··]

异常处理

摘要:异常处理在任何系统中都是重要的组成部分,Netty 的异常处理是通过在 ChannelHandler 中重写 exceptionCaught 方法来实现,这篇文章聚焦于此。

Netty 版本:4.1.70.Final

一、异常处理方式

1、捕获异常处理

1    public static class InboundHandler extends ChannelInboundHandlerAdapter {
2        @Override
3        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
4            // 处理异常
5            System.err.println(this.getClass().getSimpleName() + " ---- " + cause.getMessage());
6            // 向下一个handler传递异常
7            ctx.fireExceptionCaught(cause);
8        }
9    }

P.S. 传递异常将从下个 handler 一直往后传递,不会跳过 OutboundHandler。


2、通过监听 Funture、Promise 处理

1)在调研writewriteAndFlush可以获得得到 ChannelFeature,进而可以监听执行结果是否成功、异常。通常在 InboundHandler 中使用。

 1            ctx.executor().schedule(()->{
 2                channelFuture.addListener(new ChannelFutureListener() {
 3                    @Override
 4                    public void operationComplete(ChannelFuture future) throws Exception {
 5                        System.out.println("writeAndFlush done.");
 6                        if(future.isSuccess()){
 7                            System.out.println("send success.");
 8                        }else{
 9                       		// 处理异常
10                            System.out.println("send fail!");
11                        }
12                    }
13                });
14            }, 0, TimeUnit.SECONDS);

2)在 OutboundHandler 中,write 方法的入参会带有个Promise参数,通过 Promise 对象可以处理异常,处理后将通知之前的 handler。使用时通过setSuccesssetFailure设置。

三、源码分析

1、是谁触发 exceptionCaught 方法

主要是 AbstractChannelhandlerContext#invokeExceptionCaught 方法。

 1    private void invokeExceptionCaught(final Throwable cause) {
 2        if (invokeHandler()) {
 3            try {
 4                //
 5                handler().exceptionCaught(this, cause);
 6            } catch (Throwable error) {
 7                if (logger.isDebugEnabled()) {
 8                    logger.debug(
 9                        "An exception {}" +
10                        "was thrown by a user handler's exceptionCaught() " +
11                        "method while handling the following exception:",
12                        ThrowableUtil.stackTraceToString(error), cause);
13                } else if (logger.isWarnEnabled()) {
14                    logger.warn(
15                        "An exception '{}' [enable DEBUG level for full stacktrace] " +
16                        "was thrown by a user handler's exceptionCaught() " +
17                        "method while handling the following exception:", error, cause);
18                }
19            }
20        } else {
21            fireExceptionCaught(cause);
22        }
23    }

当 channel 触发事件调用 invokeChannelActive、invokeChannelRead 过程中出现异常调用 invokeExceptionCaught。

 1    private void invokeChannelActive() {
 2        if (invokeHandler()) {
 3            try {
 4                ((ChannelInboundHandler) handler()).channelInactive(this);
 5            } catch (Throwable t) {
 6            	// 处理异常
 7                invokeExceptionCaught(t);
 8            }
 9        } else {
10            fireChannelInactive();
11        }
12    }
13
14    private void invokeChannelRead(Object msg) {
15        if (invokeHandler()) {
16            try {
17                ((ChannelInboundHandler) handler()).channelRead(this, msg);
18            } catch (Throwable t) {
19            	// 处理异常
20                invokeExceptionCaught(t);
21            }
22        } else {
23            fireChannelRead(msg);
24        }
25    }

2、如果我们创建的 Handler 不处理异常,那么会由 Pipeline 中名为tail 的 ChannelHandlerContext 来捕获异常并打印。

可以看到 DefaultChannelPipeline 中有两个内部类:TailContext、HeadContext,最后的异常就是被 TailContext 的实力tail处理的,可以看到 onUnhandledInboundException 方法中使用 warn 级别输出了异常信息。

 1   // A special catch-all handler that handles both bytes and messages.
 2    final class TailContext extends AbstractChannelHandlerContext implements ChannelInboundHandler {
 3
 4        TailContext(DefaultChannelPipeline pipeline) {
 5            super(pipeline, null, TAIL_NAME, TailContext.class);
 6            setAddComplete();
 7        }
 8
 9        @Override
10        public void userEventTriggered(ChannelHandlerContext ctx, Object evt) {
11            onUnhandledInboundUserEventTriggered(evt);
12        }
13
14        @Override
15        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
16            // 处理最后的异常,方法如下
17            onUnhandledInboundException(cause);
18        }
19        // 省略...
20    }
21
22    /**
23     * 处理最后的异常
24     * Called once a {@link Throwable} hit the end of the {@link ChannelPipeline} without been handled by the user
25     * in {@link ChannelHandler#exceptionCaught(ChannelHandlerContext, Throwable)}.
26     */
27    protected void onUnhandledInboundException(Throwable cause) {
28        try {
29            logger.warn(
30                    "An exceptionCaught() event was fired, and it reached at the tail of the pipeline. " +
31                            "It usually means the last handler in the pipeline did not handle the exception.",
32                    cause);
33        } finally {
34            ReferenceCountUtil.release(cause);
35        }
36    }
37
38    final class HeadContext extends AbstractChannelHandlerContext
39            implements ChannelOutboundHandler, ChannelInboundHandler {
40
41        private final Unsafe unsafe;
42
43        HeadContext(DefaultChannelPipeline pipeline) {
44            super(pipeline, null, HEAD_NAME, HeadContext.class);
45            unsafe = pipeline.channel().unsafe();
46            setAddComplete();
47        }
48
49        @Override
50        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
51            ctx.fireExceptionCaught(cause);
52        }
53        // 省略...
54    }

EOF


博客没有评论系统,可以通过 邮件 评论和交流。 Top↑