NOTE
2.92 异常事件
1. 准备实验数据 - NettyServer - InboundHandlerA - InboundHandlerB - OutboundHandlerA - OutboundHandlerB 2. 开始debug 在 com.zsk.server.handler.InboundHandlerA#
这是历史学习笔记,可能存在过时或不完整的理解。
1. 准备实验数据
- NettyServer
package com.zsk.server;
import com.zsk.server.handler.*;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
/**
* @description:
* @author: zsk
* @create: 2019-12-05 19:14
**/
public class NettyServer
{
public static void main(String[] args)
{
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
try
{
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup,workerGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 100)
.childHandler(new ChannelInitializer<SocketChannel>()
{
@Override
protected void initChannel(SocketChannel channel) throws Exception
{
ChannelPipeline pipeline = channel.pipeline();
pipeline.addLast(new InboundHandlerA())
.addLast(new InboundHandlerB())
.addLast(new OutboundHandlerA())
.addLast(new OutboundHandlerB());
}
});
ChannelFuture future = bootstrap.bind(8000).sync();
future.channel().closeFuture().sync();
}
catch (InterruptedException e)
{
e.printStackTrace();
}
finally
{
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
- InboundHandlerA
package com.zsk.server.handler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
/**
* @description:
* @author: zsk
* @create: 2019-12-10 20:41
**/
public class InboundHandlerA extends ChannelInboundHandlerAdapter
{
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception
{
throw new RuntimeException("custom error");
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception
{
System.out.println(this.getClass().getSimpleName() + "catch exception:" + cause.getMessage());
ctx.fireExceptionCaught(cause);
}
}
- InboundHandlerB
package com.zsk.server.handler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
/**
* @description:
* @author: zsk
* @create: 2019-12-10 20:41
**/
public class InboundHandlerB extends ChannelInboundHandlerAdapter
{
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception
{
System.out.println(this.getClass().getSimpleName() + "catch exception:" + cause.getMessage());
ctx.fireExceptionCaught(cause);
}
}
- OutboundHandlerA
package com.zsk.server.handler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelOutboundHandlerAdapter;
import io.netty.channel.ChannelPromise;
/**
* @description:
* @author: zsk
* @create: 2019-12-10 23:08
**/
public class OutboundHandlerA extends ChannelOutboundHandlerAdapter
{
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception
{
System.out.println(this.getClass().getSimpleName() + "catch exception:" + cause.getMessage());
ctx.fireExceptionCaught(cause);
}
}
- OutboundHandlerB
package com.zsk.server.handler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelOutboundHandlerAdapter;
import io.netty.channel.ChannelPromise;
import java.util.concurrent.TimeUnit;
/**
* @description:
* @author: zsk
* @create: 2019-12-10 23:08
**/
public class OutboundHandlerB extends ChannelOutboundHandlerAdapter
{
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception
{
System.out.println(this.getClass().getSimpleName() + "catch exception:" + cause.getMessage());
ctx.fireExceptionCaught(cause);
}
}
2. 开始debug
在com.zsk.server.handler.InboundHandlerA#exceptionCaught上打个断点启动,然后使用nc连接
3. 首先被InboundHandlerA捕获
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception
{
System.out.println(this.getClass().getSimpleName() + "catch exception:" + cause.getMessage());
ctx.fireExceptionCaught(cause);
}
打印消息之后,通过ctx.fireExceptionCaught继续往后传播异常
3.1. 通过AbstractChannelHandlerContext#fireExceptionCaught传播异常
io.netty.channel.AbstractChannelHandlerContext#fireExceptionCaught
public ChannelHandlerContext fireExceptionCaught(final Throwable cause) {
//这里
invokeExceptionCaught(next, cause);
return this;
}
next是InboundHandlerB,说明直接把异常丢给下一个节点
- invokeExceptionCaught
static void invokeExceptionCaught(final AbstractChannelHandlerContext next, final Throwable cause) {
ObjectUtil.checkNotNull(cause, "cause");
EventExecutor executor = next.executor();
if (executor.inEventLoop()) {
//这里
next.invokeExceptionCaught(cause);
} else {
try {
executor.execute(new Runnable() {
@Override
public void run() {
next.invokeExceptionCaught(cause);
}
});
} catch (Throwable t) {
if (logger.isWarnEnabled()) {
logger.warn("Failed to submit an exceptionCaught() event.", t);
logger.warn("The exceptionCaught() event that was failed to submit was:", cause);
}
}
}
}
- invokeExceptionCaught
private void invokeExceptionCaught(final Throwable cause) {
if (invokeHandler()) {
try {
//这里
handler().exceptionCaught(this, cause);
} catch (Throwable error) {
if (logger.isDebugEnabled()) {
logger.debug(
"An exception {}" +
"was thrown by a user handler's exceptionCaught() " +
"method while handling the following exception:",
ThrowableUtil.stackTraceToString(error), cause);
} else if (logger.isWarnEnabled()) {
logger.warn(
"An exception '{}' [enable DEBUG level for full stacktrace] " +
"was thrown by a user handler's exceptionCaught() " +
"method while handling the following exception:", error, cause);
}
}
} else {
fireExceptionCaught(cause);
}
}
走到handler().exceptionCaught(this, cause);其实就是进入了InboundHandlerB的exceptionCaught方法
4. 然后被InboundHandlerB捕获
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception
{
System.out.println(this.getClass().getSimpleName() + "catch exception:" + cause.getMessage());
ctx.fireExceptionCaught(cause);
}
逻辑同InboundHandlerA,打印异常后,继续通过ctx.fireExceptionCaught继续往后传播异常
4.1. 通过AbstractChannelHandlerContext#fireExceptionCaught传播异常
同上面的逻辑,只不过这次的下一个节点是OutboundHandlerA
5. 接着被OutboundhandlerA捕获
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception
{
System.out.println(this.getClass().getSimpleName() + "catch exception:" + cause.getMessage());
ctx.fireExceptionCaught(cause);
}
逻辑同InboundHandlerA,打印异常后,继续通过ctx.fireExceptionCaught继续往后传播异常
5.1. 通过AbstractChannelHandlerContext#fireExceptionCaught传播异常
同上面的逻辑,只不过这次的下一个节点是OutboundHandlerB
6. 再被OutboundHandlerB捕获
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception
{
System.out.println(this.getClass().getSimpleName() + "catch exception:" + cause.getMessage());
ctx.fireExceptionCaught(cause);
}
逻辑同InboundHandlerA,打印异常后,继续通过ctx.fireExceptionCaught继续往后传播异常
6.1. 通过AbstractChannelHandlerContext#fireExceptionCaught传播异常
同上面的逻辑,只不过这次的下一个节点是io.netty.channel.DefaultChannelPipeline.TailContext#exceptionCaught
7. 最后被TailContext捕获
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
onUnhandledInboundException(cause);
}
- onUnhandledInboundException
protected void onUnhandledInboundException(Throwable cause) {
try {
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,那么会打印警告信息,最后释放资源2