1. 设计模式 - 责任链模式
1. 概念讲解
-
责任链模式(Chain of Responsibility Pattern)为请求创建了一个处理对象的链。
-
发起请求和具体处理请求的过程解耦:职责链上的处理者负责处理请求,客户只需要将请求发送到职责链上即可,无需关心请求的处理细节和请求的传递。
2. 实现责任链模式
- 实现责任链模式 4 个要素:处理器抽象类、具体的处理器实现类、保存处理器信息、处理执行。
3. 责任链代码演示
// --- 链表形式调用 --- netty 就是类似的这种形式
public class PipelineDemo {
/**
* 初始化的时候造一个 head,作为责任链的开始,但是并没有具体处理
*/
public HandlerChainContext head = new HandlerChainContext(new AbstractHandler() {
@Override
void doHandler(HandlerChainContext handlerChanContext, Object arg0) {
handlerChanContext.runNext(arg0);
}
});
public void addLast(AbstractHandler handler) {
HandlerChainContext context = head;
while (context.next != null) {
context = context.next;
}
context.next = new HandlerChainContext(handler);
}
public void requestProcess(Object arg0) {
this.head.handler(arg0);
}
public static void main(String[] args) {
PipelineDemo piplineDemo = new PipelineDemo();
piplineDemo.addLast(new Handler2());
piplineDemo.addLast(new Handler1());
piplineDemo.addLast(new Handler1());
piplineDemo.addLast(new Handler2());
piplineDemo.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 handlerChanContext, 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);
}
}
2. Netty 中的 ChannelPipeline 责任链
- Pipeline 管道保存了通道所有处理器信息。
- 创建新 Channel 时自动创建一个专有的 pipeline。
- 入站事件和出站操作会调用 pipeline 上的处理器。
1. 入站事件和出站事件
-
入站事件:通常指 I/O 线程生成了入站数据。(通俗理解:从 socket 底层自己往上冒上来的事件都是入站)比如 EventLoop 收到 selector 的 OP_READ 事件,入站处理器调用 socketChannel.read(ByteBuffer) 接收到数据后,这将导致通道的 ChannelPipeline 中包含的下一个中的 channelRead 方法被调用。
-
出站事件:通常是指 I/O 程序执行实际的输出操作。(通俗理解:想主动往 socket 底层操作的事件都是出站)比如 bind 方法用意是请求 server socket 绑定到给定的 SocketAddress ,这将导致通道的 ChannePipeline 中包含的下一个出站处理器中的 bind() 方法被调用。
2. Netty 中事件的定义
3. Pipeline 的 handler 是什么
-
ChannelHandler:用于处理 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 都是通过上下文对象。
4. 维护 Pipeline 中的 handler
- ChannelPipeline 是线程安全的,ChannelHandler 可以在任何时候添加或删除。
- 例如,你可以在即将交换敏感信息时插入加密处理程序,并在交换后删除它。
- 一般操作,初始化的时候添加进去,较少删除。
5. handler 的执行分析
- 当入站事件时,执行顺序是 1、2、3、4、5
- 当出站事件时,执行顺序是 5、4、3、2、1
- 在这一原则之上,ChannelPipeline 在执行时会进行选择
- 3 和 4 为出站处理器,因此入站事件的实际执行是:1、2、5
- 1 和 2 为入站处理器,因此出站事件的实际执行是:5、4、3
- 不同的入站事件会出发 handler 不通的方法执行:上下文对象中 fire** 开头的方法,代表入站事件传播和处理,其余的方法代表出站事件的传播和处理。