深入理解SimpleChannelInboundHandler

 

因项目需要,需要了解 Netty 这款号称 "高性能Java网络编程" 框架。拿起一本《Netty In Action》开始研究,在第2章的例子中,发现 Echo 服务端使用的ChannelHandler是 ChannelInboundHandlerAdapter ,而 Echo 客户端使用的却是 SimpleChannelInboundHandler 。一脸茫然,不知所措,只能点进去看各自的实现原理。

一.SimpleChannelInboundHandler

    首先看到的是 SimpleChannelInboundHandler 继承自 ChannelInboundHandlerAdapter。

public abstract class SimpleChannelInboundHandler<I> extends ChannelInboundHandlerAdapter{ ... }

    有图有真相:

    既然是继承关系,也就是说,"你有的我也有,你没有的我还有。" 那么 SimpleChannelInboundHandler 里面肯定重写或者新增了 ChannelInboundHandlerAdapter 里面的方法功能 - channelRead0 和 channelRead()。

protected abstract void channelRead0(ChannelHandlerContext ctx, I msg) throws Exception;

    这里只提供了一个模板,作用是把处理逻辑不变的内容写好在 channelRead(ctx,msg) 中,并且在里面调用 channelRead0 ,这样变化的内容通过抽象方法实现传递到子类中去了(在Netty5中channelRead0已被重命名为messageReceived)。

    @Override// 继承了 ChannelInboundHandlerAdapter#channelRead
    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);// 释放资源(保存消息的ByteBuf)
            }
        }
    }

    为什么这样做?看看《Netty In Action》的原话:

在客户端,当 channelRead0() 方法完成时,你已经有了传入消息,并且已经处理完它了。当该方法返回时,SimpleChannelInboundHandler负责释放指向保存该消息的ByteBuf的内存引用。

    那为什么服务端不需要这样处理呢?

在EchoServerHandler中,你仍然需要将传入消息回送给发送者,而 write() 操作是异步的,直到 channelRead() 方法返回后可能仍然没有完成。为此,EchoServerHandler扩展了 ChannelInboundHandlerAdapter ,其在这个时间点上不会释放消息。

    啥意思,我的理解是 ChannelInboundHandlerAdapter 不会像 SimpleChannelInboundHandler 一样在 channelRead() 里面释放资源,而是在收到消息处理完成的事件时,才会释放资源,看下面的代码就能理解了。

二.ChannelInboundHandlerAdapter

    EchoServerHandler#channelReadComplete,这是一个EchoServer小例子:

    public void channelReadComplete(ChannelHandlerContext ctx)
            throws Exception {
        //将未决消息冲刷到远程节点,并且关闭该 Channel
        ctx.writeAndFlush(Unpooled.EMPTY_BUFFER)
                .addListener(ChannelFutureListener.CLOSE);
    }

    消息在 channelReadComplete() 方法中,当 writeAndFlush() 方法被调用时才被释放,我们点进去源码验证一下:

    AbstractChannelHandlerContext#writeAndFlush,如下所示,最后的资源是在 writeAndFlush() 中被释放的。

    public ChannelFuture writeAndFlush(Object msg) {
        return writeAndFlush(msg, newPromise());// 跳转到另外一个重载的方法中
    }
    public ChannelFuture writeAndFlush(Object msg, ChannelPromise promise) {
        if (msg == null) {// msg不能为空
            throw new NullPointerException("msg");
        }
        if (isNotValidPromise(promise, true)) {
            ReferenceCountUtil.release(msg);// 释放资源(保存消息的ByteBuf)
            // cancelled
            return promise;
        }
        write(msg, true, promise);// 异步写操作
        return promise;
    }

三.扩展 - ReferenceCountUtil

    上面的源码中,最后资源是通过 ReferenceCountUtil 来释放的。也就是说,当我们需要释放ByteBuf相关内存的时候,也可以使用 ReferenceCountUtil#release()。

    ReferenceCountUtil 底层实现是 ReferenceCounted ,当新的对象初始化的时候计数为1,retain() 方法被调用时引用计数加1,release()方法被调用时引用计数减1,当计数减少到0的时候会被显示清除,再次访问被清除的对象会出现访问冲突(这里想起了JVM判断对象是否存活的引用计数算法)。

    ReferenceCountUtil#release:

    public static boolean release(Object msg) {
        if (msg instanceof ReferenceCounted) {
            return ((ReferenceCounted) msg).release();// Decreases the reference count by 1
        }
        return false;
    }

四.参考

  Netty进阶之路第三章:Netty内存池泄漏疑云案例

 

  • 8
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
SimpleChannelInboundHandler是一个抽象类,继承自ChannelInboundHandlerAdapter。\[1\]它是Netty框架中的一个关键组件,用于处理入站消息。SimpleChannelInboundHandlerChannelInboundHandlerAdapter的基础上新增了或重写了一些方法,其中最重要的是channelRead0方法。这个方法用于处理接收到的消息,并且只会处理特定类型的消息,而不需要手动释放资源。\[2\] 在Netty中,SimpleChannelInboundHandler通常用于客户端,而ChannelInboundHandlerAdapter通常用于服务端。这是因为SimpleChannelInboundHandler在处理完消息后会自动释放资源,而ChannelInboundHandlerAdapter需要手动释放资源。所以在Echo示例中,服务端使用ChannelInboundHandlerAdapter,而客户端使用SimpleChannelInboundHandler。\[3\] 总结来说,SimpleChannelInboundHandler是Netty框架中用于处理入站消息的抽象类,它继承自ChannelInboundHandlerAdapter并新增了一些方法,其中最重要的是channelRead0方法。在实际应用中,SimpleChannelInboundHandler通常用于客户端,而ChannelInboundHandlerAdapter通常用于服务端。 #### 引用[.reference_title] - *1* *2* *3* [深入理解SimpleChannelInboundHandler](https://blog.csdn.net/u010739551/article/details/89496731)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值