netty 的异常处理

1. io.netty.util.IllegalReferenceCountException: refCnt: 0, decrement: 1

io.netty.util.IllegalReferenceCountException: refCnt: 0, decrement: 1
	at io.netty.buffer.AbstractReferenceCountedByteBuf.release0(AbstractReferenceCountedByteBuf.java:101)
	at io.netty.buffer.AbstractReferenceCountedByteBuf.release(AbstractReferenceCountedByteBuf.java:89)
	at io.netty.util.ReferenceCountUtil.release(ReferenceCountUtil.java:84)
	at io.netty.channel.SimpleChannelInboundHandler.channelRead(SimpleChannelInboundHandler.java:112)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:340)
	at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1334)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348)
	at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:926)
	at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:134)
	at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:644)
	at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:579)
	at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:496)
	at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:458)
	at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:858)
	at io.netty.util.concurrent.DefaultThreadFactory$DefaultRunnableDecorator.run(DefaultThreadFactory.java:138)
	at java.lang.Thread.run(Thread.java:745)

该异常原因是因为,一个对象被多次执行release。

出错代码分析:

自己写的handler

public class XDecoder extends SimpleChannelInboundHandler {
    // 消息头长度
    public final int BASE_LENGTH = XLetter.LENGTH;
    private int headerIndex;
    private int bodyIndex;
    // 消息头数据
    private byte[] header;
    // 消息体数据
    private byte[] body;
    // 日志操作类
    private Logger logger;

    public XDecoder(Logger logger) {
        super();
        this.logger = logger;
        this.header = new byte[BASE_LENGTH];
        initReadNewMessage();
    }
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, ByteBuf byteBuf) throws Exception {
        try {
            NettyChannel.channelMap.get(ctx.channel()).updateReadTime();
            read(ctx, byteBuf);
        } catch (Exception e) {
            logger.warn("received a error message. ", e);
        }
        // 因为该配置下SimpleChannelInboundHandler的autoRelease是true,所以ChannelRead方法在调用ChannelRead0方法后会执行
        // ReferenceCountUtil.release(byteBuf)。如果这里也释放一次会出现一个对象多次释放,就会报
        // io.netty.util.IllegalReferenceCountException: refCnt: 0, decrement: 1 异常。
        finally {
            ReferenceCountUtil.release(byteBuf);
        }
    }

自己写的代码里有

ReferenceCountUtil.release(byteBuf);

这里释放了一次,然后我们看我们继承的父类中

public abstract class SimpleChannelInboundHandler extends ChannelInboundHandlerAdapter {
@Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        boolean release = true;
        try {
            if (acceptInboundMessage(msg)) {
                @SuppressWarnings("unchecked")
                I imsg = (I) msg;
                channelRead0(ctx, imsg);
            } else {
                release = false;
                ctx.fireChannelRead(msg);
            }
        } finally {
            if (autoRelease && release) {
                ReferenceCountUtil.release(msg);
            }
        }
    }

    /**
     * Please keep in mind that this method will be renamed to
     * {@code messageReceived(ChannelHandlerContext, I)} in 5.0.
     *
     * Is called for each message of type {@link I}.
     *
     * @param ctx           the {@link ChannelHandlerContext} which this {@link SimpleChannelInboundHandler}
     *                      belongs to
     * @param msg           the message to handle
     * @throws Exception    is thrown if an error occurred
     */
    protected abstract void channelRead0(ChannelHandlerContext ctx, I msg) throws Exception;
父类的channelRead方法中在调用了channelRead0的时候,还会判断要不要执行
ReferenceCountUtil.release(byteBuf);

这是后通过代码查看到

public abstract class SimpleChannelInboundHandler extends ChannelInboundHandlerAdapter {

    private final TypeParameterMatcher matcher;
    private final boolean autoRelease;

    /**
     * see {@link #SimpleChannelInboundHandler(boolean)} with {@code true} as boolean parameter.
     */
    protected SimpleChannelInboundHandler() {
        this(true);
    }

    /**
     * Create a new instance which will try to detect the types to match out of the type parameter of the class.
     *
     * @param autoRelease   {@code true} if handled messages should be released automatically by passing them to
     *                      {@link ReferenceCountUtil#release(Object)}.
     */
    protected SimpleChannelInboundHandler(boolean autoRelease) {
        matcher = TypeParameterMatcher.find(this, SimpleChannelInboundHandler.class, "I");
        this.autoRelease = autoRelease;
    }

我们使用的是父类的默认构造方法,所以这里的autoRealse为true的。就会出现同一个对象进行两次的release。

同样的道理,我们在

SimpleChannelInboundHandler 

要判断是否要release外,在OutboundHandler中也要判断是否要release,而且要保证不会多次被release。

你可能感兴趣的:(java)