使用Netty5.0的一些问题

2018-03-09  本文已影响0人  岁月静好丶丶丶

1.在NettyClientHandler中的ChannelRead()读取数据时报IllegalReferenceCountException异常

问题:NettyClientHandler继承的SimpleChannelInboundHandler<Object>类,在执行ChannelRead()中的buf.readBytes(bs)时报以下错误:

DefaultChannelPipeline: 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.
                                                                               io.netty.util.IllegalReferenceCountException: refCnt: 0
                                                                                   at io.netty.buffer.AbstractByteBuf.ensureAccessible(AbstractByteBuf.java:1150)
                                                                                   at io.netty.buffer.AbstractByteBuf.checkReadableBytes(AbstractByteBuf.java:1133)
                                                                                   at io.netty.buffer.AbstractByteBuf.readBytes(AbstractByteBuf.java:648)
                                                                                   at io.netty.buffer.AbstractByteBuf.readBytes(AbstractByteBuf.java:656)
                                                                                   at com.xianfan.jt808demo.connect.NettyClientHandler.channelRead(NettyClientHandler.java:59)
                                                                                   at io.netty.channel.ChannelHandlerInvokerUtil.invokeChannelReadNow(ChannelHandlerInvokerUtil.java:84)
                                                                                   at io.netty.channel.DefaultChannelHandlerInvoker.invokeChannelRead(DefaultChannelHandlerInvoker.java:153)
                                                                                   at io.netty.channel.PausableChannelEventExecutor.invokeChannelRead(PausableChannelEventExecutor.java:86)
                                                                                   at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:389)
                                                                                   at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:956)
                                                                                   at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:127)
                                                                                   at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:514)
                                                                                   at io.netty.channel.nio.NioEventLoop.processSelectedKeysPlain(NioEventLoop.java:433)
                                                                                   at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:387)
                                                                                   at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:351)
                                                                                   at io.netty.util.concurrent.SingleThreadEventExecutor$2.run(SingleThreadEventExecutor.java:116)
                                                                                   at io.netty.util.internal.chmv8.ForkJoinTask$RunnableExecuteAction.exec(ForkJoinTask.java:1412)
                                                                                   at io.netty.util.internal.chmv8.ForkJoinTask.doExec(ForkJoinTask.java:280)
                                                                                   at io.netty.util.internal.chmv8.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:877)
                                                                                   at io.netty.util.internal.chmv8.ForkJoinPool.scan(ForkJoinPool.java:1706)
                                                                                   at io.netty.util.internal.chmv8.ForkJoinPool.runWorker(ForkJoinPool.java:1661)
                                                                                   at io.netty.util.internal.chmv8.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:126)

原因:在netty4中,对象的生命周期由引用计数器控制,ByteBuf就是如此,每个对象的初始化引用计数为1,调用一次release方法,引用计数器会减1,当尝试访问计数器为0的,对象时,会抛出IllegalReferenceCountException,正如ensureAccessible的实现,更加详细的解释可以参考官方文档
AbstractByteBuf.java

protected final void ensureAccessible() {
        if (refCnt() == 0) {
            throw new IllegalReferenceCountException(0);
        }
    }

NettyClientHandler类中的 super.channelRead(ctx, msg);这行代码。追踪调用路径

private void invokeChannelRead(Object msg) {
        try {
            ((ChannelInboundHandler) handler()).channelRead(this, msg);
        } catch (Throwable t) {
            notifyHandlerException(t);
        }
    }

最终调用的代码是:ReferenceCountUtil.release(msg)

public static boolean release(Object msg) {
        if (msg instanceof ReferenceCounted) {
            return ((ReferenceCounted) msg).release();
        }
        return false;
    }

也就是每次super.channelRead(ctx, msg);后,ByteBuf就会调用release()方法,计数器减一,然后在 buf.readBytes(butfs);这行代码就会校验ensureAccessible(),计数器为0,netty认为ByteBuf对象已经释放,就抛出异常。
解决方法:去掉NettyClientHandler中这行代码 super.channelRead(ctx, msg);
ByteBuf对象谁处理谁释放。

上一篇 下一篇

猜你喜欢

热点阅读