引言
本文次要介绍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 的回调办法执行程序如下:
handlerAdded()
channelRegistered()
channelActive()
channelRead()
channelReadComplete()
Channel连贯办法回调
依据下面的执行程序,这里补充介绍回调办法的含意。
-
逻辑处理器被增加:
handlerAdded()
检测到新连贯,调用
ch.pipleLine(new LifeCycleTestHandlerTest())
之后的回调,示意以后的Channel胜利绑定一个逻辑处理器。 -
channel 绑定到线程 (NioEventLoop):
channelRegistered()
示意以后Channel所有逻辑处理器和某个线程模型的线程绑定实现,比方绑定到NIO模型的NioEventLoop。此时会创立线程解决连贯的读写,具体应用了JDK的线程池实现,通过线程池抓一个线程形式绑定。
-
channel 准备就绪:
channelActive()
触发条件是所有的业务逻辑筹备实现,也就是pipeline增加了所有的Handler ,以及绑定好一个NIO的线程之后,相当于是worker全副就位,就等boss一声令下动工。
-
channel 有数据可读
channelRead()
客户端每次发数据都会以此进行回调。
-
channel 某次数据读写实现
channelReadComplete()
服务端每次解决以此数据都回调此办法进行回调 。
Channel 敞开办法回调
-
channel 被敞开:
channelInactive()
TCP层曾经转为非ESTABLISH模式。
-
channel勾销线程 (NioEventLoop) 的绑定:
channelUnregistered()
与连接线程的绑定全副勾销时候触发。
-
逻辑处理器被删除:
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
利用的场景如下:
- TCP建设和连贯形象
-
实现统计单机的连贯数量
- 其中channelActive被调用,对应的每次调用连贯+1
- 其中channelInActive被调用,对应的每次调用连贯-1
-
实现客户端IP黑白名单的连贯过滤
channelRead()
这个办法和粘包和拆包问题相干,服务端依据自定义协定拆包,在这个办法中每次读取肯定数据,都会累加到容器当中。
channelReadComplete
每次向客户端写入数据都调用writeAndFlush
不是很高效, 更好的实现是向客户端写数据只write
,然而在ChannelReadComplete
外面一次 flush ,所以这个办法相当于批量刷新机制,批量刷新谋求更高性能。
总结
- 本局部次要是介绍ChannelHandler的各种回调,以及连贯建设敞开,执行回调是一个逆向过程。
- 每一种回调都有各自用法,然而局部回调界线比拟含糊,更多须要在实践中辨别和应用。
写到最初
生命周期和回调在Netty中十分直观,本文更多是对于重要的办法进行列举。
发表回复