《跟闪电侠学Netty》阅读笔记 - ChannelHandler 生命周期

简介: 《跟闪电侠学Netty》阅读笔记 - ChannelHandler 生命周期

引言

本文主要介绍ChannelHandler当中的ChannelInboundHandler。

思维导图

www.mubu.com/doc/1lK922R…

image.png

LifeCycleTestHandler 案例

首先来看一下案例,LifeCycleTestHandlerTest 利用适配器 ChannelInboundHandlerAdapter 重写,重写相关方法。

  • public void handlerAdded(ChannelHandlerContext ctx) throws Exception
  • public void channelRegistered(ChannelHandlerContext ctx) throws Exception
  • public void channelActive(ChannelHandlerContext ctx) throws Exception
  • public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception
  • public void channelReadComplete(ChannelHandlerContext ctx) throws Exception
  • public void channelInactive(ChannelHandlerContext ctx) throws Exception
  • public void channelUnregistered(ChannelHandlerContext ctx) throws Exception
  • public void handlerRemoved(ChannelHandlerContext ctx) throws Exception


/**
 * 测试netty channelHandler 的生命周期
 *
 * 对应的是 ChannelInboundHandlerAdapter
 */
@Slf4j
public class LifeCycleTestHandlerTest extends ChannelInboundHandlerAdapter {
    public static void main(String[] args) {
        ServerBootstrap serverBootstrap = new ServerBootstrap();
        NioEventLoopGroup boss = new NioEventLoopGroup();
        NioEventLoopGroup worker = new NioEventLoopGroup();
        serverBootstrap
                .group(boss, worker)
                .channel(NioServerSocketChannel.class)
                .childHandler(new ChannelInitializer<NioSocketChannel>() {
                    @Override
                    protected void initChannel(NioSocketChannel ch) throws Exception {
                        ch.pipeline().addLast(new StringDecoder());
                        ch.pipeline().addLast(new LifeCycleTestHandlerTest());
                    }
                })
                .bind(8001);
    }
    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        log.info("逻辑处理器被添加 handlerAdded() ");
        super.handlerAdded(ctx);
    }
    @Override
    public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
        log.info("channel 绑定到线程 (NioEventLoop):channelRegistered() ");
        super.channelRegistered(ctx);
    }
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        log.info("channel 准备就绪 channelActive()");
        super.channelActive(ctx);
    }
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        log.info("channel 某次数据读写完成 channelReadComplete() ");
        super.channelReadComplete(ctx);
    }
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        log.info("channel 有数据可读 channelRead() ");
        super.channelRead(ctx, msg);
    }
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        log.info("channel 被关闭 channelInactive()");
        super.channelInactive(ctx);
    }
    @Override
    public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
        log.info("channel取消线程 (NioEventLoop) 的绑定:channelUnregistered() ");
        super.channelUnregistered(ctx);
    }
    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        log.info("逻辑处理器被删除 handlerRemoved() ");
        super.handlerRemoved(ctx);
    }
}

如果启动Client客户端并且进行连接,ChannelHanlder 的回调方法执行顺序如下:

  1. handlerAdded()
  2. channelRegistered()
  3. channelActive()
  4. channelRead()
  5. channelReadComplete()

Channel连接方法回调

根据上面的执行顺序,这里补充介绍回调方法的含义。

  1. 逻辑处理器被添加:handlerAdded()  
    检测到新连接,调用ch.pipleLine(new LifeCycleTestHandlerTest()) 之后的回调,表示当前的Channel成功绑定一个逻辑处理器
  2. channel 绑定到线程 (NioEventLoop):channelRegistered()  
    表示当前Channel所有逻辑处理器和某个线程模型的线程绑定完成,比如绑定到NIO模型的NioEventLoop。此时会创建线程处理连接的读写,具体使用了JDK的线程池实现,通过线程池抓一个线程方式绑定。
  3. channel 准备就绪: channelActive()
    触发条件是所有的业务逻辑准备完成,也就是pipeline添加了所有的Handler ,以及绑定好一个NIO的线程之后,相当于是worker全部就位,就等boss一声令下开工。
  4. channel 有数据可读 channelRead()
    客户端每次发数据都会以此进行回调。
  5. channel 某次数据读写完成 channelReadComplete()
    服务端每次处理以此数据都回调此方法进行回调 。

Channel 关闭方法回调

  1. channel 被关闭:channelInactive()
    TCP层已经转为非ESTABLISH模式。
  2. channel取消线程 (NioEventLoop) 的绑定:channelUnregistered()
    与连接线程的绑定全部取消时候触发。
  3. 逻辑处理器被删除:handlerRemoved()
    移除连接对应的所有业务逻辑处理器触发。

Channel连接和关闭回调流程图

image.png

ChannelHanlder用法举例

ChannelInitializer

入门程序当中的 .childHandler(new ChannelInitializer()定义了抽象initChannel方法,抽象方法需要自行实现,通常是服务端启动流程的逻辑处理器中使用,添加Handler链到Pipeline。

handlerAdded() 方法和 channelRegistered()方法都会尝试调用 initChannel,initChannel被多次调用,利用putIfAbsent防止initChannel被多次调用。


if (initMap.putIfAbsent(ctx, Boolean.TRUE) == null) { // Guard against re-entrance.

思考:为什么既要防止被多次调用,Netty又要调用多次initChannel

1. handlerAdded方法已经通过Channel进行绑定。 2. 用户自行覆盖和重写ChannelInitializerhandlerAdded,导致Channel不触发绑定

3. channelRegisted() 中再触发一次绑定,并且本身不允许被重写。

public final void channelRegistered(ChannelHandlerContext ctx) throws Exception {

4. 引导实现者只关注重写handlerAdded之后的逻辑处理

handlerAdded 与 channelInActive

主要用做资源释放和申请。

channelActive 和 ChannelInActive

应用的场景如下:

  1. TCP建立和连接抽象
  2. 实现统计单机的连接数量
  • 其中channelActive被调用,对应的每次调用连接+1
  • 其中channelInActive被调用,对应的每次调用连接-1
  1. 实现客户端IP黑白名单的连接过滤

channelRead()

这个方法和粘包和拆包问题相关,服务端根据自定义协议拆包,在这个方法中每次读取一定数据,都会累加到容器当中。

channelReadComplete

每次向客户端写入数据都调用writeAndFlush不是很高效,  更好的实现是向客户端写数据只write,但是在ChannelReadComplete里面一次 flush ,所以这个方法相当于批量刷新机制,批量刷新追求更高性能。

总结

  1. 本部分主要是介绍ChannelHandler的各种回调,以及连接建立关闭,执行回调是一个逆向过程。
  2. 每一种回调都有各自用法,但是部分回调界限比较模糊,更多需要在实践中区分和使用。

写到最后

生命周期和回调在Netty中非常直观,本文更多是对于重要的方法进行罗列。


相关文章
|
7月前
|
Java API 容器
《跟闪电侠学Netty》阅读笔记 - 数据载体ByteBuf
《跟闪电侠学Netty》阅读笔记 - 数据载体ByteBuf
139 0
|
7月前
|
缓存 网络协议 算法
《跟闪电侠学Netty》阅读笔记 - Netty入门程序解析
《跟闪电侠学Netty》阅读笔记 - Netty入门程序解析
220 0
|
4月前
|
Java 调度
Netty运行原理问题之ChannelHandler在Netty中扮演什么角色
Netty运行原理问题之ChannelHandler在Netty中扮演什么角色
|
6月前
|
移动开发 前端开发 网络协议
技术笔记:Netty专题(六)
技术笔记:Netty专题(六)
42 0
|
7月前
|
消息中间件 缓存 Java
《跟闪电侠学Netty》阅读笔记 - 开篇入门Netty
《跟闪电侠学Netty》阅读笔记 - 开篇入门Netty
182 0
|
7月前
|
编解码 开发者
Netty Review - 深入理解Netty: ChannelHandler的生命周期与事件处理机制
Netty Review - 深入理解Netty: ChannelHandler的生命周期与事件处理机制
149 0
|
7月前
|
开发工具 git
网络编程(三)netty学习demo和笔记和推荐的4本书
网络编程(三)netty学习demo和笔记和推荐的4本书
134 0
|
存储 缓存 NoSQL
跟着源码学IM(十一):一套基于Netty的分布式高可用IM详细设计与实现(有源码)
本文将要分享的是如何从零实现一套基于Netty框架的分布式高可用IM系统,它将支持长连接网关管理、单聊、群聊、聊天记录查询、离线消息存储、消息推送、心跳、分布式唯一ID、红包、消息同步等功能,并且还支持集群部署。
13521 1
|
7月前
|
消息中间件 Oracle Dubbo
Netty 源码共读(一)如何阅读JDK下sun包的源码
Netty 源码共读(一)如何阅读JDK下sun包的源码
140 1
|
NoSQL Java Redis
跟着源码学IM(十二):基于Netty打造一款高性能的IM即时通讯程序
关于Netty网络框架的内容,前面已经讲了两个章节,但总归来说难以真正掌握,毕竟只是对其中一个个组件进行讲解,很难让诸位将其串起来形成一条线,所以本章中则会结合实战案例,对Netty进行更深层次的学习与掌握,实战案例也并不难,一个非常朴素的IM聊天程序。 原本打算做个多人斗地主练习程序,但那需要织入过多的业务逻辑,因此一方面会带来不必要的理解难度,让案例更为复杂化,另一方面代码量也会偏多,所以最终依旧选择实现基本的IM聊天程序,既简单,又能加深对Netty的理解。
175 1