Pipeline之异常传播

异常传播Demo

启动类

public final class Server {

    public static void main(String[] args) throws Exception {
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .childOption(ChannelOption.TCP_NODELAY, true)
                    .childAttr(AttributeKey.newInstance("childAttr"), "childAttrValue")
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        public void initChannel(SocketChannel ch) {
                            ch.pipeline().addLast(new InBoundHandlerA());
                            ch.pipeline().addLast(new InBoundHandlerB());
                            ch.pipeline().addLast(new InBoundHandlerC());
                            ch.pipeline().addLast(new OutBoundHandlerA());
                            ch.pipeline().addLast(new OutBoundHandlerB());
                            ch.pipeline().addLast(new OutBoundHandlerC());
                            // 定义统一异常处理器
//                            ch.pipeline().addLast(new ExceptionCaughtHandler());
                        }
                    });

            ChannelFuture f = b.bind(8888).sync();

            f.channel().closeFuture().sync();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}

 InBoundHandlerA

public class InBoundHandlerA extends ChannelInboundHandlerAdapter {
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        System.out.println("InBoundHandlerA.exceptionCaught()");
        ctx.fireExceptionCaught(cause);
    }
}

InBoundHandlerB

在channelRead方法中抛出了自定义异常。 

public class InBoundHandlerB extends ChannelInboundHandlerAdapter {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        throw new BusinessException("from InBoundHandlerB");
    }
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        System.out.println("InBoundHandlerB.exceptionCaught()");
        ctx.fireExceptionCaught(cause);
    }
}

BusinessException自定义异常类

public class BusinessException extends Exception {

    public BusinessException(String message) {
        super(message);
    }
}

InBoundHandlerC 

public class InBoundHandlerC extends ChannelInboundHandlerAdapter {
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        System.out.println("InBoundHandlerC.exceptionCaught()");
        ctx.fireExceptionCaught(cause);
    }
}

OutBoundHandlerA 

public class OutBoundHandlerA extends ChannelOutboundHandlerAdapter {

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        System.out.println("OutBoundHandlerA.exceptionCaught()");
        ctx.fireExceptionCaught(cause);
    }
}

OutBoundHandlerB 

public class OutBoundHandlerB extends ChannelOutboundHandlerAdapter {
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        System.out.println("OutBoundHandlerB.exceptionCaught()");
        ctx.fireExceptionCaught(cause);
    }
}

OutBoundHandlerC 

public class OutBoundHandlerC extends ChannelOutboundHandlerAdapter {

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        System.out.println("OutBoundHandlerC.exceptionCaught()");
        ctx.fireExceptionCaught(cause);
    }
}

 启动服务端,通过“telnet 127.0.0.1 8888”,输入“send hello”测试一下调用结果:

从结果可以看到,异常传播的顺序与handler添加的顺序是一致的,与是否是inbound还是outbound无关,如果传播过程中,各个节点都没有处理异常的话,异常最终会被传递到tail节点,tail节点会打印警告日志并释放资源。

        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
            onUnhandledInboundException(cause);
        }

    protected void onUnhandledInboundException(Throwable cause) {
        try {
            // 异常被传播到了pipeline的tail节点,这意味着pipeline中最后一个handler没有处理异常
            logger.warn(
                    "An exceptionCaught() event was fired, and it reached at the tail of the pipeline. " +
                            "It usually means the last handler in the pipeline did not handle the exception.",
                    cause);
        } finally {
            ReferenceCountUtil.release(cause);
        }
    }

定义异常处理器

通过tail节点给出的警告信息可以知道,我们在向pipeline添加handler的时候,需要在最后添加一个异常处理器,否则异常就会进入到tail节点,这样我们是无法控制异常处理的逻辑的。

因此我们定义一个统一的异常处理器,在最后添加到pipeline(将启动类中的注释打开)。

public class ExceptionCaughtHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        try {
            // 统一的异常处理
            if (cause instanceof BusinessException) {
                System.out.println("BusinessException");
            }
        } finally {
            ReferenceCountUtil.release(cause);
        }
    }
}

重新测试一下:

 可以看到异常在传播到ExceptionCaughtHandler时就会被处理了,不会继续传播到tail节点。

发布了153 篇原创文章 · 获赞 106 · 访问量 19万+

猜你喜欢

转载自blog.csdn.net/u011212394/article/details/103970521