Mina过滤器链原理
Mina中的过滤器,位于ioService和ioHandler之间,用于在正式的业务处理之前,做一些额外的预处理或者过滤。比如,编解码过滤器,日志过滤器,心跳过滤器等。下图是mina官网上的一个mina应用的结构图:
多个过滤器共同组成过滤器链,这篇文章对mina中的过滤器的自定义、如何进入和退出过滤器链、过滤器之间的传递、过滤器链中过滤器的顺序问题等做一个研究。
mina中默认使用的过滤器链是DefaultIoFilterChain,对于过滤器和过滤器链,基本上所有的需要了解的关键都在这个类里面。
1. 自定义过滤器。
这个问题不是本篇的重点,简单说一下就行。比如,我们需要在mina应用中插入日志功能,mina中有一个现在的LoggingFilter,插入即可:
DefaultIoFilterChain.addLast(new LoggingFilter())
2. 如何进入过滤器链
比如说,服务端要写数据到客户端,流程是什么时候进入过滤器链的?
要发送数据,首先在handler里面调用iosession.write(msg), 稍微一跟踪,就会发现,write方法中,调用了过滤器链的fireFilterWrite()方法:
filterChain.fireFilterWrite(writeRequest);
这就进入了过滤器链,然后接着跟踪,最终会按顺序调用一个个过滤器的filterWrite()方法:
filter.filterWrite(nextFilter, session, writeRequest);
在查看DefaultIoFilterChain类中的源码,fire开头的方法很多,如fireMessageReceived、fireSessionClosed,。。。这些方法的意思也很明显,触发相关的事件。
3. 如何退出过滤器链
现在考虑一个问题,一般的过滤器,他们的事件处理方法中,处理完之后,一般都会调用下一个过滤器的处理方法,如:LoggingFilter中messageReceived方法:
@Overridepublic void messageReceived(NextFilter nextFilter, IoSession session, Object message) throws Exception {log(messageReceivedLevel, "RECEIVED: {}", message);nextFilter.messageReceived(session, message);}
我们知道,所有过滤器都处理完了之后,messageReceived会转交给IoHandler处理,这件事应该是最后一个过滤器来做,但是自己的业务代码中从来没有写过把消息转交给handler的工作。猜测:DefaultIoFilterChain中应该有一个最后把关的过滤器,来做这个事情,所以翻开代码继续查看:
DefaultIoFilterChain有一个head和tail,构造DefaultIoFilterChain时,对这两个成员进行了初始化:
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;
}
head和tail是一个EntryImpl,里面包含filter。从这里看出head包含一个HeadFilter,tail包含一个TailFilter。
根据猜测,TailFilter是最后一个filter,那么我们看看他的messageReceived是怎么写的:
session.getHandler().messageReceived(s, message);
大部分省略,上面这句是重点,调用了handler的方法,猜测得到验证。
配合DefaultIoFilterChain的addLast、addFirst等方法,我们发现,不管我们往过滤器链中插入了多少过滤器,head始终作为第一个节点,而tail始终作为最后一个。
至于head有什么作为,下面会讲。
4. 过滤器的传递方向问题
继续说write,写数据进入过滤器链中,调用fireFilterWrite方法,在这个方法中调用了另一个方法:callPreviousFilterWrite,这个方法有点奇怪,调用前一个filterWrite方法?
在其他的fire方法中,调用的是next,比如fireMessageReceived中,调用了callNextMessageReceived,调用下一个的messageReceived方法。那为什么在fireFilterWrite 中,要调用前一个的filterWrite方法呢?方法的逻辑是否真和方法名所表明的一样呢?
public void fireFilterWrite(WriteRequest writeRequest) {callPreviousFilterWrite(tail, session, writeRequest);}private void callPreviousFilterWrite(Entry entry, IoSession session, WriteRequest writeRequest) {try {IoFilter filter = entry.getFilter();NextFilter nextFilter = entry.getNextFilter();filter.filterWrite(nextFilter, session, writeRequest);} catch (Exception e) {writeRequest.getFuture().setException(e);fireExceptionCaught(e);} catch (Error e) {writeRequest.getFuture().setException(e);fireExceptionCaught(e);throw e;}}
查看源码发现,fireFilterWrite方法确实是从tail开始的,首先调用了tail中的filter的filterWrite方法,跟踪发现Tail的filterWrite方法中又调用了他的下一个filter的filterWrite:
@Override
public void filterWrite(NextFilter nextFilter, IoSession session, WriteRequest writeRequest) throws Exception {nextFilter.filterWrite(session, writeRequest);
}
不仅如此,其他继承了IoFilterAdapter的普通filter,也都是调用了nextFilter.filterWrite(session, writeRequest); 既然调用的是nextFilter,为什么叫callPreviousFilterWrite?
在创建每个过滤器entry的时候,构造了他的nextFilter,我们发现,每个过滤器的构造,都是一个EntryImpl对象,我们看看这个对象的源码,构造方法中有这么一段:
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 inputClosed(IoSession session) {Entry nextEntry = EntryImpl.this.nextEntry;callNextInputClosed(nextEntry, session);}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);}public String toString() {return EntryImpl.this.nextEntry.name;}};
这里定义了nextFilter, 注意这里面filterWrite方法和 filterClose方法的不同:
Entry nextEntry = EntryImpl.this.prevEntry;
callPreviousFilterWrite(nextEntry, session, writeRequest);
把prevEntry赋给了nextENtry, 与messageReceived刚好相反,这样就一清二楚了,所谓的nextEntry其实是prevEntry。那么我们继续猜测,在写的时候,head将会是最后一个过滤器,负责把消息交给下层的ioProcessor处理,看看HeadFilter的源码:
private class HeadFilter extends IoFilterAdapter {@SuppressWarnings("unchecked")@Overridepublic void filterWrite(NextFilter nextFilter, IoSession session, WriteRequest writeRequest) throws Exception {AbstractIoSession s = (AbstractIoSession) session;// Maintain counters.if (writeRequest.getMessage() instanceof IoBuffer) {IoBuffer buffer = (IoBuffer) writeRequest.getMessage();// I/O processor implementation will call buffer.reset()// it after the write operation is finished, because// the buffer will be specified with messageSent event.buffer.mark();int remaining = buffer.remaining();if (remaining > 0) {s.increaseScheduledWriteBytes(remaining);}} else {s.increaseScheduledWriteMessages();}WriteRequestQueue writeRequestQueue = s.getWriteRequestQueue();if (!s.isWriteSuspended()) {if (writeRequestQueue.isEmpty(session)) {// We can write directly the messages.getProcessor().write(s, writeRequest);} else {s.getWriteRequestQueue().offer(s, writeRequest);s.getProcessor().flush(s);}} else {s.getWriteRequestQueue().offer(s, writeRequest);}}@SuppressWarnings("unchecked")@Overridepublic void filterClose(NextFilter nextFilter, IoSession session) throws Exception {((AbstractIoSession) session).getProcessor().remove(session);}}
重点是下面这句:
if (writeRequestQueue.isEmpty(session)) {// We can write directly the messages.getProcessor().write(s, writeRequest);
} else {s.getWriteRequestQueue().offer(s, writeRequest);s.getProcessor().flush(s);
}
再次得到证实。
总结:
我们发现,NextFilter中,只有filterClose方法和filterWrite相同,把prevEntry作为nextEntry。
这种现象的本质是,写和连接关闭的事件,在过滤器链中的传递方向,与其他事件的传递方向是刚好相反的,这是为什么?
上图F1、F2、F3是过滤器链中的三个过滤器,事件流的方向有A和B两种方向,服务端向客户端写数据,是一种handler主动的操作,在handler中调用session的write方法,所以事件流是A方向,而当有消息收到时,事件流的方向是B,其他类似。
再假设,F1和F2是两个不同的编解码器,当有消息来时,首先经过F2解码,再经过F1解码,最后交给handler处理。那么,当要发送消息的时候,要对消息进行编码,是不是应该先经过F1编码,然后才让F2编码,如果反了,客户端解码就得不到正确结果。当然,这样做是基于服务端和客户端具有完全相同的过滤器链(包括过滤器顺序也要相同), 这是一个基本原则。
Mina过滤器链原理相关推荐
- Java过滤器链原理解析
在很多Java Web项目中我们会在web.xml中配置一些过滤器来拦截请求,比如下面解决乱码的编码过滤器: <filter><filter-name>encodingFilt ...
- SpringSecurity过滤器链加载原理
SpringSecurity过滤器链加载原理 通过前面十五个过滤器功能的介绍,对于SpringSecurity简单入门中的疑惑是不是在心中已经有了答案了呀? 但新的问题来了!我们并没有在web.xml ...
- Servlet→对象监听器、事件监听器、Session钝化活化、@WebListener标注、过滤器概念原理生命周期、过滤器链、@WebFilter标注、定时器Timer、cancel()、purge
监听器ServletContextListener HttpSessionListener ServletRequestListener 事件监听器 Session钝化活化 @WebListener标 ...
- Dubbo之Filter链原理
本文来说下Dubbo的Filter链原理 文章目录 概述 构造Filter链 Consumer ConsumerContextFilter ActiveLimitFilter FutureFilter ...
- 安全认证框架Shiro (二)- shiro过滤器工作原理
安全认证框架Shiro (二)- shiro过滤器工作原理 安全认证框架Shiro 二- shiro过滤器工作原理 第一前言 第二ShiroFilterFactoryBean入口 第三请求到来解析过程 ...
- Spring Security之过滤器链【探案】+源码剖析
Spring Security之过滤器链[探案] Spring Security常用过滤器介绍 过滤器是一种典型的AOP思想,关于什么是过滤器,就不赘述了,接下来咱们就一起看看Spring Secur ...
- 关于vue过滤器的原理解析
又来学习源码系列,今天就看一下vue中的过滤器具体是怎么实现的,我觉得这是一个不常用但是很重要的知识点,开冲! 01 前言 过滤器实质不改变原始数据,只是对数据进行加工处理后返回过滤后的数据再进行调用 ...
- Java Servlet中Filter过滤器的原理以及使用方式
详细介绍了Java Web Servlet中的Filter过滤器的原理以及常见用法. 文章目录 1 Filter接口 1.1 过滤器的生命周期 1.2 doFilter过滤方法 2 Filter的使用 ...
- 算法:详解布隆过滤器的原理、使用场景和注意事项@知乎.Young Chen
算法:详解布隆过滤器的原理.使用场景和注意事项@知乎.Young Chen 什么是布隆过滤器 本质上布隆过滤器是一种数据结构,比较巧妙的概率型数据结构(probabilistic data struc ...
最新文章
- vs2012与win7不兼容问题
- 部署SAP HANA之前你需要知道的那些事
- standford lessons
- CSDN博客图片水印|自定义水印|去除水印
- django model中的DateField()转为时间戳
- 转 8天入门wpf—— 第六天 细说控件
- Flex 4中组件背景设置(填充方式)group为例子
- python是什么-Python 是什么
- 法学类计算机专业,就业蓝皮书:计算机类专业领跑薪酬榜 法学专业被亮“红牌”...
- 团队建立伊始的混沌状态(Thinking in CTO)
- 网易云音乐推荐系统特训_笔记
- 客户端性能测试工具-Wetest、cude PC
- 肖博高考数学二轮复习方法之立体几何解题策略-付题型解析
- LVM -逻辑卷管理
- Excel 轻松制作 二级联动 下拉列表清单
- android手机邮件6,Android手机邮箱设置详细教程
- 原装安装版WIN10安装详细教程
- 用华为ENSP做一个关于防火墙的小实验-华为eNSP怎样调出右侧接口列表
- 数据库,计算机网络、操作系统刷题笔记20
- css及css3学习