一起说说Netty责任链,责任链这块是netty运行机制的核心,一起来完整的了解下netty的责任链。
(一)责任链模式
- ① 介绍
责任链模式(Chain of Responsibility Pattern) 为请求创建了一个处理对象的链。一个请求过来后,就交给一个责任链进行调用。在责任链里面定义很多很多的handler,具体请求这个程序的handler,请求者不关心,多少个步骤,多少次,只负责发送到责任链上,请求传递的细节不关心。
- ② 实现责任链模式
处理器抽象类,具体的处理器实现类,保存处理器信息,处理执行。
- ③ 源码分析
// -----链表形式调用------netty就是类似的这种形式 public class PipelineDemo { /** * 初始化的时候造一个head,作为责任链的开始,但是并没有具体的处理 */ public HandlerChainContext head = new HandlerChainContext(new AbstractHandler() { @Override void doHandler(HandlerChainContext handlerChainContext, Object arg0) { handlerChainContext.runNext(arg0); } }); public void requestProcess(Object arg0) { this.head.handler(arg0); } public void addLast(AbstractHandler handler) { HandlerChainContext context = head; while (context.next != null) { context = context.next; } context.next = new HandlerChainContext(handler); } public static void main(String[] args) { PipelineDemo pipelineChainDemo = new PipelineDemo(); pipelineChainDemo.addLast(new Handler2()); pipelineChainDemo.addLast(new Handler1()); pipelineChainDemo.addLast(new Handler1()); pipelineChainDemo.addLast(new Handler2()); // 发起请求 pipelineChainDemo.requestProcess("火车呜呜呜~~"); } } /** * handler上下文,我主要负责维护链,和链的执行 */ class HandlerChainContext { HandlerChainContext next; // 下一个节点 AbstractHandler handler; public HandlerChainContext(AbstractHandler handler) { this.handler = handler; } void handler(Object arg0) { this.handler.doHandler(this, arg0); } /** * 继续执行下一个 */ void runNext(Object arg0) { if (this.next != null) { this.next.handler(arg0); } } } // 处理器抽象类 abstract class AbstractHandler { /** * 处理器,这个处理器就做一件事情,在传入的字符串中增加一个尾巴.. */ abstract void doHandler(HandlerChainContext handlerChainContext, Object arg0); // handler方法 } // 处理器具体实现类 class Handler1 extends AbstractHandler { @Override void doHandler(HandlerChainContext handlerChainContext, Object arg0) { arg0 = arg0.toString() + "..handler1的小尾巴....."; System.out.println("我是Handler1的实例,我在处理:" + arg0); // 继续执行下一个 handlerChainContext.runNext(arg0); } } // 处理器具体实现类 class Handler2 extends AbstractHandler { @Override void doHandler(HandlerChainContext handlerChainContext, Object arg0) { arg0 = arg0.toString() + "..handler2的小尾巴....."; System.out.println("我是Handler2的实例,我在处理:" + arg0); // 继续执行下一个 handlerChainContext.runNext(arg0); } }
(二)Netty中的ChannelPipeline责任链
- ① 介绍
pipeline管道保存了通道所有处理器信息,创建channel时自动创建一个专有的pipeline,入站事件和出站事件会调用pipeline上的处理器。
- ② 入站事件和出站事件
入站事件:通常指IO线程生成了入站数据
(通俗理解:从socket底层自己往上冒上来的事件都是入站)
比如EventLoop收到selector的OP_READ事件,入站处理器调用socketChannel.read(ByteBuffer)接受到数据后,这将导致通道的ChannelPipeline中包含的下一个中的channelRead方法被调用
出站事件:通常指IO线程执行实际的输出操作
(通俗理解:想主动往socket底层操作的事件的都是出站)
比如bind方法用意时请求server socket绑定到给定的SocketAddress,这将导致通道的ChannelPipeline中包含的下一个出站处理器中的bind方法被调用
- ③ Nettty中定义的事件
- ④ Pipeline中的handler是什么
ChannelHeadler
用于处理I/O事件或者拦截I/O操作,并转发到ChannelPipeline中下一个处理器。这个顶级接口定义功能很弱,实际使用时会去实现下面两大子接口:处理入站I/O事件的ChannelInboundHandler、处理出站I/O操作的ChannelOutboundHandler
适配器类
为了方便开发,避免所有handler去实现一遍接口方法,Netty提供了简单的实现类:
ChannelInboundHandlerAdapter处理入站I/O事件
ChannelOutboundHandlerAdapter处理出站I/O事件
ChannelDuplexHandler支持同时处理入站和出站事件
ChannelHandlerContext
实际存储在Pipeline中的并非是ChannelHandler,而是上下文对象。将Handler包裹在上下文对象中,通过上下文对象与它所属的ChannelPipeline交互,向上或向下传递事件或者修改pipeline都是通过上下文对象。
- ⑤ 那么如何维护Pipeline中的handler呢
ChannelPipeline是线程安全的,ChannelHandler可以在任何时候添加或者删除。例如你可以在即将交换敏感信息时插入加密处理程序,并在交换后删除它。一般操作,初始化的时候增加进去,较少删除。下面是Pipeline中管理的API
除了register方法还有bind方法,bind方法时出站事件执行顺序和入站事件相反
请求过来以后又是如何处理的呢?我们通过Accept事件获取请求,所以我们应该去看accept入站事件是如何处理的,
PS:用户在管道中有一个或者多个channelhandler来接收I/O事件(例如读取)和请求I/O操作(例如写入和关闭)一个典型的服务器在每个通道的管道中都有以下处理程序,但是根据协议和业务逻辑的复杂性和特征,可能会有所不同。
协议解码器--将二进制数据(例如ByteBuf)转换为Java对象
协议编码器--将java对象转化为二进制数据
业务逻辑处理程序--执行实际的业务逻辑(例如访问数据库)