我在51cto写文章,只是为了记录自己的记忆,毕竟好记性不如烂笔头。不是为了博眼球,吸引粉丝。

这篇文章,我来讲解下NIO里的2个重要的概念:pipeline和handlers。

在设计模式里面提到过一种责任链的模式,责任链模式简单说就是击鼓传花,一个任务,顺着链按个触发。想详细了解责任链模式的读者,请自己百度,设计模式不在这篇文章讨论范围之内。

如果把数据比做水,那么pipeline就是水管,水管上的阀门就是处理水的部分。比如一个Request请求进来,可能经过层层的处理;响应也是,所以类似这种服务端的框架很适合使用pipeline来解决。

wKioL1hbCcKAI3H9AAFAOi1SdUM886.png


其中的HandlerContext就是上面讲的阀门,包括拦截器模式,Filter模式它们的实现都是类似的,全部是基于责任链的变体。每个HandlerContext都聚合一个Handler,由Handler负责具体的逻辑处理。

我们再来看下Netty中ChannelHandler的类图:

wKiom1hbCgLyOc3fAAA80nOMfSc668.jpg

ChannelHandler有两个子类ChannelInboundHandler和ChannelOutboundHandler,这两个类对应了两个数据流向,如果数据是从外部流入我们的应用程序,我们就看做是inbound,相反便是outbound。一个ChannelHandler处理完接收到的数据会传给下一个Handler,或者什么不处理,直接传递给下一个--责任链模式。

wKioL1hbCh6j6F9PAABPvXWKaVc644.jpg

上图中我们可以看到,一个ChannelPipeline可以把两种Handler(ChannelInboundHandler和ChannelOutboundHandler)混合在一起,当一个数据流进入ChannelPipeline时,它会从ChannelPipeline头部开始传给第一个ChannelInboundHandler,当第一个处理完后再传给下一个,一直传递到管道的尾部。与之相对应的是,当数据被写出时,它会从管道的尾部开始,先经过管道尾部的“最后”一个ChannelOutboundHandler,当它处理完成后会传递给前一个ChannelOutboundHandler。

这里需要注意:

1)Netty中,可以注册多个handler。ChannelInboundHandler按照注册的先后顺序执行;ChannelOutboundHandler按照注册的先后顺序逆序执行。

2)ChannelInboundHandler之间的传递,通过调用 ctx.fireChannelRead(msg) 实现;而调用ctx.write(msg) 将传递到ChannelOutboundHandler。ctx.write()方法执行后,需要调用flush()方法才能令它立即执行。

3)ChannelOutboundHandler 在注册的时候需要放在最后一个ChannelInboundHandler之前,否则将无法传递到ChannelOutboundHandler。

InboundHandler和OutboundHandler在ChannelPipeline中是混合在一起的,那么它们如何区分彼此呢?其实很容易,因为它们各自实现的是不同的接口,对于inbound event,Netty会自动跳过OutboundHandler;相反若是outbound event,ChannelInboundHandler会被忽略掉。

最后,我们再来总结下几个概念:

1)不管是server还是client,每个连接都会对应一个pipeline,该pipeline在Channel被创建的时候创建。

2)ChannelPipeline是ChannelHandler的容器,它包含了一个ChannelHander形成的列表,且所有ChannelHandler都会注册到ChannelPipeline中。其中ChannelHander用于处理连接的Inbound或者Outbound事件。

3)ChannelPipeline,它维护了一个有序的ChannelHandler列表,但并非是直接关联,而是通过维护ChannelHandlerContext进行关联。

4)一个ChannelHandlerContext只能对应一个ChannelHander,只对应一个Channel;而一个ChannelHander则可以对应多个ChannelHandlerContext。