netty应该怎样处理异常
netty 在ChannelInBouindHandler中为我们提供了exceptionCaught()方法,看下源码来看看他在什么情况下会被调用
首先点开 ctx.fireChannelRead(msg);
的源码查看,发现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
@Override
public ChannelHandlerContext fireChannelRead(final Object msg) {
invokeChannelRead(findContextInbound(MASK_CHANNEL_READ), msg);
return this;
}
static void invokeChannelRead(final AbstractChannelHandlerContext next, Object msg) {
final Object m = next.pipeline.touch(ObjectUtil.checkNotNull(msg, "msg"), next);
EventExecutor executor = next.executor();
if (executor.inEventLoop()) {
next.invokeChannelRead(m);
} else {
executor.execute(new Runnable() {
@Override
public void run() {
next.invokeChannelRead(m);
}
});
}
}
private void invokeChannelRead(Object msg) {
if (invokeHandler()) {
try {
((ChannelInboundHandler) handler()).channelRead(this, msg);
} catch (Throwable t) {
notifyHandlerException(t);
}
} else {
fireChannelRead(msg);
}
}
上面代码会在pinline中查找下一个inHandler,并回掉他的ChannelRead方法,且用try catch处理回掉过程。
如果报错会调用notifyHandlerException(t)
方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
private void notifyHandlerException(Throwable cause) {
if (inExceptionCaught(cause)) {
if (logger.isWarnEnabled()) {
logger.warn(
"An exception was thrown by a user handler " +
"while handling an exceptionCaught event", cause);
}
return;
}
invokeExceptionCaught(cause);
}
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);
}
}
notifyHandlerException(t)
方法会调用handler()
即当前handler的exceptionCaught(this, cause);
方法
如果当前handler实现ChannelInboundHandlerAdapter且没实现exceptionCaught方法的话,默认会调用 ctx.fireExceptionCaught(cause)
,即下一个handler的exceptionCaught
方法。
总结
netty中执行handler报错时,会先调用当前handler的exceptionCaught方法,如果没有当前没重写exceptionCaught方法会调用下一个handler的exceptionCaught方法。 所以如果错误需要在当前handler处理,则重写exceptionCaught方法,自己选择要不要将异常传递下去, 如果当前handler不能处理,可以在pipline链最后一位放置一个重写exceptionCaught方法的handler ,放在pipline最后一位即可。
有一种情况下,ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE
,这是一个监听器,它会将错误从head节点向下传播。