Mina中的过滤器处于IoService与IoHandler之间,用于过滤每一个I/O事件。本文分析Mina中的过滤器是怎么串起来的?

前面提到了IoFilter,FilterChain等接口和类,在分析过滤器链怎么串起来之前,有必要看一下这些接口和类之间的关系。

如上图所示:

FilterChain是由一个个Entry串起来的,EntryImpl是Entry的实现;

从EntryImpl中可以获取到Filter与NextFilter,NextFilter相当于那根线(指针);

有两个特殊的Entry,里面的Filter分别是HeadFilter和TailFilter,我们添加的Filter都处于这两个Filter之间;

每做完一个Entry,NextFilter会回到FilterChain处理下一个Entry(处理顺序如下图所示)。

由上图可知:

(1)当消息到来时,触发过滤器链的fireXXX事件(这一步一般在processor里面触发);

(2)从过滤器链获取上头结点Entry,从头结点Entry中取出filter和nextFilter;

(3)Filter处理后,交由nextFilter处理,nextFilter并不是一个真正的Filter,它决定这个过滤器链的走向,在这里它是返回到过滤器链;

(4)过滤器链通过nextFilter指针得到下一个entry,重复执行(2)、(3),直到每个Entry都处理完。

这里抛出两个问题:

1、如何保证新加入的过滤器在HeadFilter与TailFilter之间?

2、nextFilter一定是从左向右的顺序吗?

3、TailFilter调用了IoHandler吗?

对于第一个问题,答案在EntryImpl的构造方法里面

// EntryImpl构造方法
private EntryImpl(EntryImpl prevEntry, EntryImpl nextEntry, String name, IoFilter filter) {
......
this.prevEntry = prevEntry;
this.nextEntry = nextEntry;
this.name = name;
this.filter = filter;
this.nextFilter = new NextFilter() {
......
};
} //FilterChain构造方法
public DefaultIoFilterChain(AbstractIoSession session) {
if (session == null) {
throw new IllegalArgumentException("session");
} this.session = session;
head = new EntryImpl(null, null, "head", new HeadFilter());
tail = new EntryImpl(head, null, "tail", new TailFilter());
head.nextEntry = tail;
}

在FilterChain构造方法里面初始化了两个Entry:

我们一般调用IoFilterChain的如下方法添加过滤器

void addFirst(String name, IoFilter filter);
void addLast(String name, IoFilter filter);
void addBefore(String baseName, String name, IoFilter filter);
void addAfter(String baseName, String name, IoFilter filter);

这里以addLast为例,先看看addLast方法的实现

public synchronized void addFirst(String name, IoFilter filter) {
checkAddable(name);
register(head, name, filter);
} private void register(EntryImpl prevEntry, String name, IoFilter filter) {
EntryImpl newEntry = new EntryImpl(prevEntry, prevEntry.nextEntry, name, filter);
......
prevEntry.nextEntry.prevEntry = newEntry;
prevEntry.nextEntry = newEntry;
name2entry.put(name, newEntry);
......
}

从代码一目了然,这就是一个简单的链表操作,在这里第一个问题回答完毕。

对于第二个问题答案是否定的。从IoFilter接口可以看出,一共处理以下几种事件:

sessionCreated
sessionOpened
sessionClosed
sessionIdle
exceptionCaught
messageReceived
messageSent
filterClose
filterWrite
前面FilterChain的构造方法中,省略了NextFilter的实现,这里补上

 this.nextFilter = new NextFilter() {
public void sessionCreated(IoSession session) {
Entry nextEntry = EntryImpl.this.nextEntry;
callNextSessionCreated(nextEntry, session);
} public void sessionOpened(IoSession session) {
Entry nextEntry = EntryImpl.this.nextEntry;
callNextSessionOpened(nextEntry, session);
} public void sessionClosed(IoSession session) {
Entry nextEntry = EntryImpl.this.nextEntry;
callNextSessionClosed(nextEntry, session);
} public void sessionIdle(IoSession session, IdleStatus status) {
Entry nextEntry = EntryImpl.this.nextEntry;
callNextSessionIdle(nextEntry, session, status);
} public void exceptionCaught(IoSession session, Throwable cause) {
Entry nextEntry = EntryImpl.this.nextEntry;
callNextExceptionCaught(nextEntry, session, cause);
} public void messageReceived(IoSession session, Object message) {
Entry nextEntry = EntryImpl.this.nextEntry;
callNextMessageReceived(nextEntry, session, message);
} public void messageSent(IoSession session, WriteRequest writeRequest) {
Entry nextEntry = EntryImpl.this.nextEntry;
callNextMessageSent(nextEntry, session, writeRequest);
} public void filterWrite(IoSession session, WriteRequest writeRequest) {
Entry nextEntry = EntryImpl.this.prevEntry;
callPreviousFilterWrite(nextEntry, session, writeRequest);
} public void filterClose(IoSession session) {
Entry nextEntry = EntryImpl.this.prevEntry;
callPreviousFilterClose(nextEntry, session);
}
}

可以看出filterWrite与filterClose是反向的,其他都是正向的。

对于第三个问题,直接看TailFilter的代码即可:

public void messageReceived(NextFilter nextFilter, IoSession session, Object message) throws Exception {
AbstractIoSession s = (AbstractIoSession) session;
if (!(message instanceof IoBuffer)) {
s.increaseReadMessages(System.currentTimeMillis());
} else if (!((IoBuffer) message).hasRemaining()) {
s.increaseReadMessages(System.currentTimeMillis());
} try {
session.getHandler().messageReceived(s, message);
} finally {
if (s.getConfig().isUseReadOperation()) {
s.offerReadFuture(message);
}
}
}

确实调用了handler来处理真正的请求。

总结一下:FilterChain是由Entry组成一个链表,HeadFilter与TailFilter所在Entry分别是链表的首尾,HeadFilter与processor相连,TailFilter与handler相连,添加Filter的操作实际上是普通的链表插入操作。FilterChain可以触发多种事件,每种事件到来时Filter的顺序是正序还是倒序由nextFilter决定。

Apache Mina Filter的更多相关文章

  1. Apache MiNa 实现多人聊天室

    Apache MiNa 实现多人聊天室 开发环境: System:Windows JavaSDK:1.6 IDE:eclipse.MyEclipse 6.6 开发依赖库: Jdk1.4+.mina-c ...

  2. Apache Mina(一)

    原文链接:http://www.cnblogs.com/xuekyo/archive/2013/03/06/2945826.html Apache Mina是一个能够帮助用户开发高性能和高伸缩性网络应 ...

  3. Apache Mina 入门实例

    这个教程是介绍使用Mina搭建基础示例.这个教程内容是以创建一个时间服务器. 以下是这个教程需要准备的东西: MINA 2.0.7 Core JDK 1.5 或更高 SLF4J 1.3.0 或更高 L ...

  4. Apache Mina原理及典型例子分析

    Apache Mina ,一个高性能 Java 异步并发网络通讯框架.利用 Mina 可以高效地完成以下任务: TCP/IP 和 UDP/IP 通讯 串口通讯 VM 间的管道通讯 SSL/TLS JX ...

  5. 网络通信框架Apache MINA

    Apache MINA(Multipurpose Infrastructure for Network Applications) 是 Apache 组织一个较新的项目,它为开发高性能和高可用性的网络 ...

  6. Apache Mina入门实例

    一.mina是啥 ApacheMINA是一个网络应用程序框架,用来帮助用户简单地开发高性能和高可扩展性的网络应用程序.它提供了一个通过Java NIO在不同的传输例如TCP/IP和UDP/IP上抽象的 ...

  7. Apache Mina -2

    我们可以了解到 mina是个异步通信框架,一般使用场景是服务端开发,长连接.异步通信使用mina是及其方便的.不多说,看例子. 本次mina 使用的例子是使用maven构建的,过程中需要用到的jar包 ...

  8. Apache Mina UDP连接目标服务器地址时出现异常

    俩种情形,第一种是开始连接时候就没连上服务器:第二种是服务器关闭连接,出现的异常: 第一种: java.lang.reflect.InvocationTargetException at sun.re ...

  9. Apache Mina入门

    Mina第一次听到这个名称的时候,我以为是个MM的名字米娜,后来才知道… Apache MINA(Multipurpose Infrastructure for Network Application ...

随机推荐

  1. Spark笔记之Catalog

    一.什么是Catalog Spark SQL提供了执行sql语句的支持,sql语句是以表的方式组织使用数据的,而表本身是如何组织存储的呢,肯定是存在一些元数据之类的东西了,Catalog就是Spark ...

  2. git log查看某一个分支的提交

    如果想查看某一个分支的提交信息:git log 或者是查看分支名:git log $分支名/tag名/远程分支名 查看提交的详情: git log -p

  3. py-faster-rcnn代码阅读2-config.py

    简介  该文件指定了用于fast rcnn训练的默认config选项,不能随意更改,如需更改,应当用yaml再写一个config_file,然后使用cfg_from_file(filename)导入以 ...

  4. JQuery效果隐藏/显示

    hide() 方法 语法 $(selector).hide(speed,callback) show() 方法 语法 $(selector).show(speed,callback) 参数 描述 sp ...

  5. springMVC初次搭建,产生错误

    七月 11, 2016 11:12:58 下午 org.apache.catalina.startup.VersionLoggerListener log 信息: Server version: Ap ...

  6. OE中的bitbake使用

    OpenEmbedded是一些脚本(shell和python脚本)和数据构成的自动构建系统.     脚本实现构建过程,包括下载(fetch).解包(unpack).打补丁(patch).config ...

  7. zabbix 3.2.2 server端(源码包)安装部署 (一)【转】

    环境准备: 操作系统 CentOS 6.8 2.6.32-642.11.1.el6.x86_64 zabbix server 172.16.10.150 zabbix agent 172.16.10. ...

  8. su: cannot set user id: Resource temporarily unavailable【转】

    今天R&D所在主机出现su: cannot set user id: Resource temporarily unavailable资源不可用报错,直接通过其他机器ssh huyuh@xxx ...

  9. shell脚本练习【转】

    1.写一个脚本,判断当前系统上所有用户的shell是否为可登录shell(即用户的shell不是/sbin/nologin):分别这两类用户的个数:通过字符串比较来实现: #脚本内容 [root@ce ...

  10. mysql及linux发行版下载源

    MySQL国内镜像资源 搜狐开源镜像站:http://mirrors.sohu.com/ 国内开源镜像站点汇总 http://segmentfault.com/a/1190000000375848   ...