关于netty:跟闪电侠学Netty阅读笔记-ChannelHandler-生命周期

108次阅读

共计 4514 个字符,预计需要花费 12 分钟才能阅读完成。

引言

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

思维导图

https://www.mubu.com/doc/1lK922R14Bl

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()

    与连接线程的绑定全副勾销时候触发。

  1. 逻辑处理器被删除:handlerRemoved()

    移除连贯对应的所有业务逻辑处理器触发。

Channel 连贯和敞开回调流程图

ChannelHanlder 用法举例

ChannelInitializer

入门程序当中的 .childHandler(new ChannelInitializer<NioSocketChannel>()定义了形象 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. 用户自行笼罩和重写 `ChannelInitializer` 的 `handlerAdded`,导致 `Channel` 不触发绑定  
3. `channelRegisted()` 中再触发一次绑定,并且自身不容许被重写。`public final void channelRegistered(ChannelHandlerContext ctx) throws Exception {`  
4. 疏导实现者只关注重写 handlerAdded 之后的逻辑解决  
                

handlerAdded 与 channelInActive

次要用做资源开释和申请。

channelActive 和 ChannelInActive

利用的场景如下:

  1. TCP 建设和连贯形象
  2. 实现统计单机的连贯数量

    • 其中 channelActive 被调用,对应的每次调用连贯 +1
    • 其中 channelInActive 被调用,对应的每次调用连贯 -1
  3. 实现客户端 IP 黑白名单的连贯过滤

channelRead()

这个办法和粘包和拆包问题相干,服务端依据自定义协定拆包,在这个办法中每次读取肯定数据,都会累加到容器当中。

channelReadComplete

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

总结

  1. 本局部次要是介绍 ChannelHandler 的各种回调,以及连贯建设敞开,执行回调是一个逆向过程。
  2. 每一种回调都有各自用法,然而局部回调界线比拟含糊,更多须要在实践中辨别和应用。

写到最初

生命周期和回调在 Netty 中十分直观,本文更多是对于重要的办法进行列举。

正文完
 1