责任链模式的使用-Netty ChannelPipeline和Mina IoFilterChain分析
本文来自网易云社区
作者:乔安然
1. Chain of Responsiblity
定义:
使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系。将这个对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理他为止。
结构实图:

2. Netty ChannelPipeline 分析
Netty的ChannelPipeline和ChannelHandler机制类似于Servlet和Filter过滤器,这类过滤器其实就是责任链模式的一种变形,方便事件的拦截和用户业务逻辑的定制且相互不必耦合在一起。
Netty将Channel的数据管道抽象为ChannelPipeline,消息在ChannelPipline中流动和传递。ChannelPipeline持有IO事件拦截器ChannelHandler的链表,由ChannelHandler对IO事件进行拦截和处理,可以方便的新增和删除ChannelHandler来实现不同的业务逻辑定制,不必对已有的ChannelHandler进行修改,这个开放闭合原则的很好体现。
下面我们对ChannelPipeline和ChannelHandler,以及相关的ChannelHandlerContext进行详细介绍和源码分析。
先看下ChannlePipeline的事件事件处理流程,如下图

- 底层Socket读取bytebuf触发ChannelRead事件(Inbound 事件),由NioEventLoop调用ChannelPipeline的fireChannelRead方法 
- 消息被ChannelPipeline中的ChannelHandlerContext传递,依次被各个ChannelHandler处理 
- 当有写出的需求(Outbound 事件),调用ChannelHandlerContext write方法,消息再通过ChannelHandlerContext反向传递通过各个ChannelHandler处理。当然各个ChannelHadler可以通过定制只对自己感兴趣的消息进行处理,其余跳过。 
下图是ChannelPipeline相关的类UML图

DefaultChannelPipeline:I/O事件承载的数据管道,由ChannelHandlerContext节点组成双链表结构
ChannelHandler: I/O事件的处理层,分别为Inbound和outbound两种事件类型派生ChannelInboundHandler和ChannelOutboundHandler接口,如上图中的MessageToMessageDecoder和MessageToMessageEncoder类分别对消息的解码和编码处理。用户在实际使用中根据需求处理Inbound还是outbound事件。
DefaultChannelHandlerContext:组成pipeline的节点,执行handler的上下文环境,支持异步模式,如下面read事件处理:
@Override
public ChannelHandlerContext fireChannelRead(final Object msg) { // 找到下一个inbound的handler
invokeChannelRead(findContextInbound(), msg); return this;
} static void invokeChannelRead(final AbstractChannelHandlerContext next, Object msg) { final Object m = next.pipeline.touch(ObjectUtil.checkNotNull(msg, "msg"), next);
EventExecutor executor = next.executor(); // 判断是否由内部触发
if (executor.inEventLoop()) {
next.invokeChannelRead(m);
} else { // 外部触发异步处理
executor.execute(new Runnable() { @Override
public void run() {
next.invokeChannelRead(m);
}
});
}
} // 触发handler中的channelRead方法,对消息进行处理
private void invokeChannelRead(Object msg) { if (invokeHandler()) { try {
((ChannelInboundHandler) handler()).channelRead(this, msg);
} catch (Throwable t) {
notifyHandlerException(t);
}
} else {
fireChannelRead(msg);
}
}
3.Mina IoFilterChain分析
责任链模式在mina中也发挥着重要的作用,其中Filter机制就是基于责任链实现的,来看下mina框架组成

从上图看到消息的接受从IoService层先经过Filter层过滤处理后最后交给IoHander,消息的发送则是反过来从IoHander层经过Filter层再到IoService层。由此可以看到netty和mina对消息处理都是相似的。
从图中看到接收消息和发送消息经过Filter层是相反处理的,那么每个Filter就必须知道前一个和后一个Filter,那么mina中的Filter层和netty的pipeline相同都是使用双向链表实现的,那么让我们来看看Filter层具体是如何实现
mina的filterchain包结构:

Filter层的每个filter都是对上图IoFilter接口的实现,我们将具体讲解IoFilter,IoFilterChain,DefaultIoFilterChain这几个类
IoFilterChainBuilder接口和DefaultIoFilterChainBuilder实现不再细讲,从字面意思就是IoFilterChain的建造者
IoFilterEvent是代表filter事件,IoFilterLifeCycleException是指加入链表异常
下面的图是我们要重点讲解的几个类的关系

IoFilter接口:NextFilter接口是其内部接口
IoFilterAdapter类:对IoFilter接口的实现,是所有Filter的基类
IoFilterChain接口:Entry接口是其内部接口
DefaultIoFilterChain类:是对IoFilterChain接口的实现,有EntryImpl,HeadFilter,TailFilter三个内部类,其中EntryImpl类中又有NextFilter接口的内部实现
还需要说明下:IoFilter还有相关接口就写了两个方法,一个接受消息触发的方法还有一个是发送消息触发的方法,剩下的都是这两类消息处理方法就不表示了,这和netty中的inbound、outbound相同
HeadFilter类只对发送消息处理方法重载,TailFilter类只对接受消息处理方法重载
从上图看到EntryImp类是重点,我们就来看看EntryImpl类的实现
private class EntryImpl implements Entry {        private EntryImpl prevEntry ;        private EntryImpl nextEntry ;        private final String name;        private IoFilter filter ;        private final NextFilter nextFilter;        private EntryImpl(EntryImpl prevEntry, EntryImpl nextEntry, String name, IoFilter filter) {            if (filter == null) {                throw new IllegalArgumentException("filter");
            }            if (name == null) {                throw new IllegalArgumentException("name");
            }            this.prevEntry = prevEntry;            this.nextEntry = nextEntry;            this.name = name;            // 业务的fliter处理层
            this.filter = filter;            // 调度filter对读入和写出消息处理
            this.nextFilter = new NextFilter() {               // 读入消息调用nextEntry处理
                public void sessionOpened(IoSession session) {
                    Entry nextEntry = EntryImpl. this.nextEntry ;
                    callNextSessionOpened(nextEntry, session);
                }                // 写出消息调用preEntry反向处理
                public void filterWrite(IoSession session, WriteRequest writeRequest) {
                    Entry nextEntry = EntryImpl. this.prevEntry ;
                    callPreviousFilterWrite(nextEntry, session, writeRequest);
                }
            };
        }
从EntryImpl类的构造方法看到,EntryImpl中保持对上一个节点和下一个节点引用,双向链表结构,name即过滤层名称,filter即过滤层的具体实现,而nextFilter是在构造方法中的内部实现。
下面我们来看看sessionOpen处理的完整过程,sessionOpen事件属于读入对应netty中的inbound事件类型。首先是IoFilterChain收到这个消息触发fireSessionOpened方法
 public void fireSessionOpened() {
        Entry head = this.head ;
        callNextSessionOpened(head, session);
    }    private void callNextSessionOpened(Entry entry, IoSession session) {        try {
            IoFilter filter = entry.getFilter();
            NextFilter nextFilter = entry.getNextFilter();
            filter.sessionOpened(nextFilter, session);
        } catch (Throwable e) {
            fireExceptionCaught(e);
        }
    }
fireSessionOpened方法获取当前的头节点,然后调用callNextSessionOpened方法,而callNextSessionOpened方法是从entry中获取filter和nextfitler,触发filter的sessionOpened方法,同时将nextfilter作为参数传进去,而filter层如果对这个消息感兴趣可以处理完成后调用nextfilter的sessionOpened方法,不感兴趣的话,可能消息到此就结束了。
由此可看出mina中的Fliter和netty的ChannelHandler功能相同,而NextFilter其实是起到中转和调度的作用,收到Reveceive消息转交给后一节点,收到Send消息转交给前一个消息。这和netty中ChannelHandlerContext功能相似。
mina和netty不相同的一点对异步多线程的使用,netty中ChannelHandlerContext中加入对异步支持,而mina中代之以一个更通用的系统,基于一个过滤器:ExecutorFilter。当Fliter层将消息事件传递到ExecutorFilter中,它包含一个Executor来将消息事件传递给线程池运行处理。
网易云免费体验馆,0成本体验20+款云产品!
更多网易研发、产品、运营经验分享请访问网易云社区。
相关文章:
【推荐】 一行代码搞定Dubbo接口调用
责任链模式的使用-Netty ChannelPipeline和Mina IoFilterChain分析的更多相关文章
- 详解java设计模式之责任链模式
		详见:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcyt175 从击鼓传花谈起 击鼓传花是一种热闹而又紧张的饮酒游戏.在酒宴上宾客依次 ... 
- mina中责任链模式的实现
		一.mina的框架回顾 责任链模式在mina中有重要的作用,其中Filter机制就是基于责任链实现的. 从上图看到消息的接受从IoService层先经过Filter层过滤处理后最后交给IoHander ... 
- Netty中的责任链模式
		适用场景: 对于一个请求来说,如果有个对象都有机会处理它,而且不明确到底是哪个对象会处理请求时,我们可以考虑使用责任链模式实现它,让请求从链的头部往后移动,直到链上的一个节点成功处理了它为止 优点: ... 
- 网络编程Netty入门:责任链模式介绍
		目录 责任链模式 责任链模式的简单实现 Netty中的ChannelPipeline责任链 服务端接收客户端连接 pipeline初始化 入站事件和出站事件 Pipeline中的Handler Pip ... 
- 设计模式学习笔记(十四)责任链模式实现以及在Filter中的应用
		责任链模式(Chain Of Responsibility Design Pattern),也叫做职责链,是将请求的发送和接收解耦,让多个接收对象都有机会处理这个请求.当有请求发生时,可将请求沿着这条 ... 
- java 设计模式 -- 责任链模式
		设计模式 – 责任链模式 介绍: 责任链模式是一种动态行为模式,有多个对象,每一个对象分别拥有其下家的引用.连起来形成一条链.待处理对象则传到此链上,在此链进行传递,且待处理对象并不知道此会被链上的哪 ... 
- java23种设计模式之十:责任链模式
		最近在学习netty中发现其中用到了责任链模式,然后结合自己在写代码中遇到了大量写if...else的情况,决定学习一下责任链模式. 一.什么样的场景下会选择用责任链模式 我们在进行业务逻辑判断时,需 ... 
- Spring是如何使用责任链模式的?
		关于责任链模式,其有两种形式,一种是通过外部调用的方式对链的各个节点调用进行控制,从而进行链的各个节点之间的切换. 另一种是链的每个节点自由控制是否继续往下传递链的进度,这种比较典型的使用方式就是Ne ... 
- 23种设计模式--责任链模式-Chain of Responsibility Pattern
		一.责任链模式的介绍 责任链模式用简单点的话来说,将责任一步一步传下去,这就是责任,想到这个我们可以相当击鼓传花,这个是为了方便记忆,另外就是我们在项目中经常用到的审批流程等这一类的场景时我们就可以考 ... 
随机推荐
- python:类与对象命名空间、面对对象的组合用法
			1,类里可以定义两种属性: #静态属性 #静态属性就是直接在类中定义的变量 #动态属性 #动态属性就是定义在类中的方法 class Course: language = ['Chinese']#静态属 ... 
- HDU 2586 How far away ?【LCA】
			任意门:http://acm.hdu.edu.cn/showproblem.php?pid=2586 How far away ? Time Limit: 2000/1000 MS (Java/Oth ... 
- c#用链表存储并读取配置文件(代码部分)
			//读取配置参数 private void loadcofig() { //List<param> camra1 = new List<param>(); List<st ... 
- logistic regression svm hinge loss
			二类分类器svm 的loss function 是 hinge loss:L(y)=max(0,1-t*y),t=+1 or -1,是标签属性. 对线性svm,y=w*x+b,其中w为权重,b为偏置项 ... 
- Restframework的认证,权限,节流
			1.认证 流程:请求到达REST framework的时候,会对request进行二次封装,在封装的过程中会对客户端发送过来的request封装进认证,选择,解析等功能.request方法封装完成之后 ... 
- golang-Tag
			Tag 理解 Golang中可以对struct定义Tag 例如: type TestTag struct{ UserName string `json:"name"` Age In ... 
- Oracle 用户、授权、角色管理
			Oracle 用户管理 一.创建用户的Profile文件SQL> create profile student limit // student为资源文件名FAILED_LOGIN_ATTEMP ... 
- jquery mobile 移动web(1)
			轻量级框架jQuery Mobile 所需文件 <link rel="stylesheet" href="jquery.mobile-1.1.2/jquery.mo ... 
- Vue--- vue-Router   路由
			文档 https://router.vuejs.org/zh-cn npm install vue-router --save 调用 import Vue from 'vue' import Vue ... 
- JDBC编程:获取数据库连接
			JDBC(Java Database Connectivity),即Java数据库连接.通过JDBC编程,可以使Java应用程序和数据库进行交互. JDBC驱动的方式有很多种,我们常用的驱动方式为:本 ... 
