适用场景:
- 对于一个请求来说,如果有个对象都有机会处理它,而且不明确到底是哪个对象会处理请求时,我们可以考虑使用责任链模式实现它,让请求从链的头部往后移动,直到链上的一个节点成功处理了它为止
优点:
- 发送者不需要知道自己发送的这个请求到底会被哪个对象处理掉,实现了发送者和接受者的解耦
- 简化了发送者对象的设计
- 可以动态的添加节点和删除节点
缺点:
- 所有的请求都从链的头部开始遍历,对性能有损耗
- 极差的情况,不保证请求一定会被处理
自定义一个责任链#
在java中不再存在指针了,如果我们想创建一个链表,只能是在本类中添加本类属性, 因为我们想创建一个链表,所以这是必须的工作
需要提供set方法,让当前的节点可以设置自己的下一个节点
处理请求的逻辑,设计成抽象方法,让不同的节点根据自己的需求去实现
public abstract class Approver { Approver approver; String name; // todo 抽象父类中可以存在构造函数,但是当我们创建子类时,必须要有一个参数的构造函数, // todo 让子类一个参数的构造函数,来给这个函数初始化 public Approver (String name){ this.name=name; } public abstract void ProcessRequest(PurchaseRequest request); // 如果当前的处理器处理不了,就会往下传播 public void setApprover( Approver approver){ this.approver=approver; } }
PurchaseRequest,需要被处理的请求,根据自己的需要各不相同
接着就是链表上的不同功能的节点都要实现上面的抽象类Approver
,重写它的抽象方法,添加上他们特定的功能
测试:
// 创建出各个节点 Approver1 approver1 = new Approver1(); Approver2 approver2 = new Approver2(); Approver3 approver3 = new Approver3(); // 设置他们关系 approver1.setApprover(approver2); approver2.setApprover(approver3); // 发起请求 Client client = new Client(); PurchaseRequest purchaseRequest = client.sendRequest(); // 处理请求 tom.ProcessRequest(purchaseRequest);
把请求传递给责任链的第一个节点,她会自动往后传播下去,直到有一个节点成功处理了它
Netty的责任链设计#
netty的pipeline设计,就采用了责任链设计模式, 底层采用双向链表的数据结构, 将链上的各个处理器串联起来
客户端每一个请求的到来,netty都认为,pipeline中的所有的处理器都有机会处理它,因此,对于入栈的请求,全部从头节点开始往后传播,一直传播到尾节点(来到尾节点的msg会被释放掉)
netty的责任链模式中的组件
- 责任处理器接口
- pipeline中的处理器都它的具体实现
- 添加删除责任处理器的接口
- 上下文
- 通过这个上下文,可以获得需要的数据,属性
- 责任终止机制
- pipeline中的每一个节点,都可以终止事件的传播
netty的责任处理器接口#
责任处理器接口, pipeline中的所有的handler的顶级抽象接口,它规定了所有的handler统一要有添加,移除,异常捕获的行为
public interface ChannelHandler { // todo 当handler被添加到真实的上下文中,并且准备处理事件时被调用 // todo handler 被添加进去的回调 void handlerAdded(ChannelHandlerContext ctx) throws Exception; // todo 是 handler 被移出的后的 回调 void handlerRemoved(ChannelHandlerContext ctx) throws Exception; @Deprecated void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception; @Inherited @Documented @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @interface Sharable { // no value } }
netty对责任处理接口,做了更细粒度的划分, 处理器被分成了两种, 一种是站处理器ChannelInboundHandler
,另一种是出站处理器ChannelOutboundHandler
,这两个接口都继承自ChannelHandler
添加删除责任处理器的接口#
netty中所有的处理器最终都在添加在pipeline上,所以,添加删除责任处理器的接口的行为 netty在channelPipeline中的进行了规定
public interface ChannelPipeline extends ChannelInboundInvoker, ChannelOutboundInvoker, Iterable<Entry<String, ChannelHandler>> { ChannelPipeline addFirst(String name, ChannelHandler handler); ChannelPipeline addFirst(EventExecutorGroup group, String name, ChannelHandler handler); ChannelPipeline addLast(String name, ChannelHandler handler); ChannelPipeline addLast(EventExecutorGroup group, String name, ChannelHandler handler); ChannelPipeline addBefore(String baseName, String name, ChannelHandler handler); ...
上下文#
pipeline中的handler被封装进了上下文中,如下, 通过上下文,可以轻松拿到当前节点所属的channel, 以及它的线程执行器
// todo AttributeMap -- 让ChannelHandlerContext 可以存储自定义的属性 // todo ChannelInboundInvoker -- 让ChannelHandlerContext 可以进行 InBound事件的传播,读事件,read 或者是 注册事件 active事件 // todo ChannelOutboundInvoker -- 让ChannelHandlerContext 可以传播写事件 public interface ChannelHandlerContext extends AttributeMap, ChannelInboundInvoker, ChannelOutboundInvoker { // todo 获取ChannelHandlerContext所对应的这个Channel对象 Channel channel(); // todo 获取事件执行器 EventExecutor executor(); ...
责任终止机制#
责任终止机制
- 在pipeline中的任意一个节点,只要我们不手动的往下传播下去,这个事件就会终止传播在当前节点
- 对于入站数据,默认会传递到尾节点,进行回收,如果我们不进行下一步传播,事件就会终止在当前节点,别忘记回收msg
- 对于出站数据,用header节点的使用unsafe对象,把数据写会客户端也意味着事件的终止
事件的传播#
底层事件的传播使用的就是针对链表的操作
private AbstractChannelHandlerContext findContextInbound() { AbstractChannelHandlerContext ctx = this; do { ctx = ctx.next; } while (!ctx.inbound); return ctx; }