mina学习笔记五:做嫁衣的IoFilter和IoListener

来源:互联网 发布:财务战略矩阵应用 编辑:程序博客网 时间:2024/05/13 12:59

   总有那么一群人,他们默默无闻的工作在自己的岗位上,你永远都接触不到他们,可是离了他们完整的做好一件事。譬如只注意在荧光屏上的明星,却不知道谁给他们打得灯光、谁给他们踩得位、谁给他们化妆端茶送水等等。IoFilter也是这群人中的一员,每个session的请求和事件离不了他们,IoFilter在IoService和IoHandler之间提供了各个层面的切面支持,让其他两个组件安心做自己的事而不用关注其他细节。软件工程的魅力也在于此——解耦。

1.IoFilter的一家子

    IoFilter家族的类图结构非常简单明快,mina非常贴心的为广大使用者提供了丰富的Filter实现子类,很多标准服务我们都不需要自己实现。

图5.1 IoFilter类图
    我们先简单介绍一下这个家族成员和他们各自的作用吧:
FilterclassDescriptionBlacklistBlacklistFilter黑名单过滤器
BufferedWriteBufferedWriteFilter发送缓存过滤器,缓存发送的消息,避免短小消息频繁发送CompressionCompressionFilter数据压缩过滤器ConnectionThrottleConnectionThrottleFilter连接控制过滤器,对同一IP地址频繁的创建连接的时间间隔进行控制ErrorGeneratingErrorGeneratingFilter花数据过滤器,增加、修改、移除接受的数据包内容,可作为加密的方式ExecutorExecutorFilter处理线程池过滤器,让每个请求或者事件都通过线程池去执行FileRegionWriteFileRegionWriteFilter文件转换过滤器,通常应该由IoProcess做这件事,但若需要压缩或者修改时,
可通过过滤器链之间的配合来实现KeepAliveKeepAliveFilter心跳包过滤器,在idle状态时发送心跳包,并能对超时进行处理LoggingLoggingFilter日志记录过滤器,最常用之一MDC InjectionMdcInjectionFilter日志信息注入过滤器,MDC(Mapped Diagnostic Context有译作线程映射表)是日志
框架维护的一组信息键值对,可向日志输出信息中插入一些想要显示的内容。NoopNoopFilter用作内部测试的filter,什么也没做ProfilerProfilerTimerFilter时间分析过滤器,记录各种事件消耗的时间ProtocolCodecProtocolCodecFilter编解码过滤器,最常用之二ProxyProxyFilter是IoConnector在连接握手时自动加入的过滤器,握手成功后就透明了Reference countingReferenceCountingFilter引用数过滤器,能记录该过滤器被加入或移除过滤器链的次数,真实使用
是继承他。RequestResponseRequestResponseFilter继承WriteRequestSessionAttributeInitializingSessionAttributeInitializingFilter初始化过滤器StreamWriteStreamWriteFilterInputStream直接转换成IoBuffer的过滤器SslFilterSslFilterTCP/IP层面的SSl加解密过滤器WriteRequestWriteRequestFilter 简化IoFilter IoEventType.WRITE事件的实现的抽象过滤器
    还有两个FIlter是mina自带并且不为用户管理的,当生成过滤器链自动注入:
  • HeadFilter(当发生write操作时,将写buffer加入到session.scheduledWriteMessages队列,并执行发送调用IoProcessor执行write()操作),位于过滤器链头。
  • TailFilter(当所有过滤器都处理完后,他将调用IoHandler的对应方法)。位于过滤器链尾。

2.IoFilter执行顺序

    搞清楚了

图5.2 过滤器调用顺序图

    在事件端,我们以messageSent事件被执行的代码来详解下过程;在操作端我们的例子是session.write。

2.1 messageSent顺序调用过滤器链

    当IoProcessor的remove(session)方法被调用时,该session将被添加进Processor内部的removingSessions的ConcurrentLinkedQueue等待被移除。
[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. public final void remove(S session) {  
  2.     scheduleRemove(session);  
  3.     startupProcessor();  
  4. }  
  5.   
  6. private void scheduleRemove(S session) {  
  7.     removingSessions.add(session);  
  8. }  
    当IoProcessor工作线程执行一个循环,调用removeSessions()方法,状态为SessionState.OPENED的session将被移除,移除的代码中就包括刷新消息队列,其中代码如下,从而引发一次过滤器链的调用。
[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. if (message instanceof IoBuffer) {  
  2.     IoBuffer buf = (IoBuffer) message;  
  3.     if (buf.hasRemaining()) {  
  4.         buf.reset();  
  5.         failedRequests.add(req);  
  6.     } else {  
  7.         IoFilterChain filterChain = session.getFilterChain();  
  8.         filterChain.fireMessageSent(req);  
  9.     }  
  10. else {  
  11.     failedRequests.add(req);  
  12. }  
    最终调用的是TailFilter,他的代码如下:
[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. private static class TailFilter extends IoFilterAdapter {  
  2.   
  3.         public void sessionCreated(NextFilter nextFilter, IoSession session) throws Exception {  
  4.             session.getHandler().sessionCreated(session);  
  5.         }  
  6.   
  7.         public void sessionOpened(NextFilter nextFilter, IoSession session) throws Exception {  
  8.             session.getHandler().sessionOpened(session);  
  9.         }  
  10.   
  11.         public void sessionClosed(NextFilter nextFilter, IoSession session) throws Exception {  
  12.             AbstractIoSession s = (AbstractIoSession) session;  
  13.             try {  
  14.                 s.getHandler().sessionClosed(session);  
  15.             } finally {  
  16.                 try {  
  17.                     s.getWriteRequestQueue().dispose(session);  
  18.                 } finally {  
  19.                     try {  
  20.                         s.getAttributeMap().dispose(session);  
  21.                     } finally {  
  22.                         try {  
  23.                             // Remove all filters.  
  24.                             session.getFilterChain().clear();  
  25.                         } finally {  
  26.                             if (s.getConfig().isUseReadOperation()) {  
  27.                                 s.offerClosedReadFuture();  
  28.                             }  
  29.                         }  
  30.                     }  
  31.                 }  
  32.             }  
  33.         }  
  34.   
  35.         public void sessionIdle(NextFilter nextFilter, IoSession session, IdleStatus status) throws Exception {  
  36.             session.getHandler().sessionIdle(session, status);  
  37.         }  
  38.   
  39.         public void exceptionCaught(NextFilter nextFilter, IoSession session, Throwable cause) throws Exception {  
  40.             AbstractIoSession s = (AbstractIoSession) session;  
  41.             try {  
  42.                 s.getHandler().exceptionCaught(s, cause);  
  43.             } finally {  
  44.                 if (s.getConfig().isUseReadOperation()) {  
  45.                     s.offerFailedReadFuture(cause);  
  46.                 }  
  47.             }  
  48.         }  
  49.   
  50.         public void messageReceived(NextFilter nextFilter, IoSession session, Object message) throws Exception {  
  51.             session.getHandler().messageReceived(s, message);  
  52.         }  
  53.   
  54.         public void messageSent(NextFilter nextFilter, IoSession session, WriteRequest writeRequest) throws Exception {  
  55.             session.getHandler().messageSent(session, writeRequest.getMessage());  
  56.         }  
  57.   
  58.         public void filterWrite(NextFilter nextFilter, IoSession session, WriteRequest writeRequest) throws Exception {  
  59.             nextFilter.filterWrite(session, writeRequest);  
  60.         }  
  61.   
  62.         public void filterClose(NextFilter nextFilter, IoSession session) throws Exception {  
  63.             nextFilter.filterClose(session);  
  64.         }  
  65.     }  


2.2 session.write逆序调用过滤器链

    在Handler中持有session的对象,调用session.write(date.toString());,session会调用具体方法:
[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. AbstractIoSession.write(Object message) {  
  2.     ...  
  3.         IoFilterChain filterChain = getFilterChain();  
  4.         filterChain.fireFilterWrite(writeRequest);  
  5.     ...  
  6.     }  
[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. DefaultIoFilterChain.fireFilterWrite(WriteRequest writeRequest) {  
  2.         Entry tail = this.tail;  
  3.         callPreviousFilterWrite(tail, session, writeRequest);  
  4.     }  
    触发逆序的过滤器链,最终到HeadFilter中:
[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. ...其他Filter  
  2. HeadFilter.filterWrite(NextFilter nextFilter, IoSession session, WriteRequest writeRequest) throws Exception {  
  3.         AbstractIoSession s = (AbstractIoSession) session;  
  4.         ...  
  5.             WriteRequestQueue writeRequestQueue = s.getWriteRequestQueue();  
  6.   
  7.             if (!s.isWriteSuspended()) {  
  8.                 if (writeRequestQueue.size() == 0) {  
  9.                     // We can write directly the message  
  10.                     s.getProcessor().write(s, writeRequest);  
  11.                 } else {  
  12.                     s.getWriteRequestQueue().offer(s, writeRequest);  
  13.                     s.getProcessor().flush(s);  
  14.                 }  
  15.             } else {  
  16.                 s.getWriteRequestQueue().offer(s, writeRequest);  
  17.             }  
  18.         }  

3.IoHandler API

    Handler虽然是直面用户的对象,但由上述可知,他其实只是整个操作链上最后的一环,也是最简单的一环,给出IoHander的API清单
  • sessionCreated(IoSession):session创建时调用
  • sessionOpened(IoSession):session打开时调用,实际上是在sessionCreated后立即执行
  • sessionClosed(IoSession):session关闭时调用
  • sessionIdle(IoSession, IdleStatus):session空闲时调用
  • exceptionCaught(IoSession, Throwable):异常时调用
  • messageReceived(IoSession, Object):接收到新的request时调用
  • messageSent(IoSession, Object):发送在消息队列中未完成的消息时调用

4. IoFilter API

   和Handler有一部分一致:
  • init() filter初始化方法,在下面4个on方法调用之前必须先被调用。
  • destroy() 该方法被ReferenceCountingFilter引用到,如果不继承ReferenceCountingFilter,不需实现该方法。
  • onPreAdd(IoFilterChain, String, NextFilter) 过滤器被添加之前调用。
  • onPostAdd(IoFilterChain, String, NextFilter) 过滤器被添加之后调用。
  • onPreRemove(IoFilterChain, String, NextFilter) 过滤器被移除之前调用。
  • onPostRemove(IoFilterChain, String, NextFilter) 过滤器被移除之后调用。
  • sessionCreated(NextFilter, IoSession) 
  • sessionOpened(NextFilter, IoSession)
  • sessionClosed(NextFilter, IoSession)
  • sessionIdle(NextFilter, IoSession, IdleStatus)
  • exceptionCaught(NextFilter, IoSession, Throwable)
  • messageReceived(NextFilter, IoSession, Object)
  • messageSent(NextFilter, IoSession, WriteRequest)
  • filterClose(NextFilter, IoSession)  session.close()方法中调用。
  • filterWrite(NextFilter, IoSession, WriteRequest) session.write()方法中调用。

5. IoListener

    上面用了比较多的篇幅介绍了IoFilter,现在我们也该讲讲二号后台人物——IoListerner。在mina中主要有两类,一个是IoServiceListener,另一个就是IoFutureListener。

5.1 IoServiceListener    

    IoServiceListener负责Service的各个生命周期节点事件通知。我们曾在学习笔记三中提到过IoServiceListenerSupport,此类对象是该service所有IoServiceListener对象的持有类并管理着监听器的回调入口,同时还管理着当前Service的所有session。当时没有详细的介绍,现在我们重点说说这个类在做嫁衣方面的独到之处。
    IoServiceListenerSupport是在AbstractIoService的构造函数中被生成的,也就是说,恭喜,无论你是Acceptor还是Connector,你都拥有这个强大的助手了。为了支持监听器的功能,可爱的祖父AbstractIoService还贴心的为我们实现好了addListener(IoServiceListener listener)和removeListener(IoServiceListener listener)方法。
    我们先看看的API吧:
  • serviceActivated(IoService) 当service生效时被调用。当新的service被bind时或第一个session生成时,IoServiceListenerSupport.fireServiceActivated被调用同时调用此方法。
  • serviceIdle(IoService, IdleStatus) 当service空闲时被调用,不过此方法没有在mina中真正使用。
  • serviceDeactivated(IoService) 当service失效时被调用。当service被unbind或最后个session注销时,IoServiceListenerSupport.fireServiceDeactivated被调用同时调用此方法。
  • sessionCreated(IoSession) 当新的session生成时被调用。IoServiceListenerSupport.fireSessionCreated
  • sessionDestroyed(IoSession) 当session被注销时调用。IoServiceListenerSupport.fireSessionDestroyed
    如此一看,还是很简单清晰的,监听器可以在任意时刻被添加或移除。我们的使用方法也很简单:
[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. IoServiceListener listener = new IoServiceListener() {  
  2.     public void sessionDestroyed(IoSession session) throws Exception {  
  3.     }  
  4.     public void sessionCreated(IoSession session) throws Exception {  
  5.     }  
  6.     public void serviceIdle(IoService service, IdleStatus idleStatus)  
  7.             throws Exception {  
  8.     }  
  9.     public void serviceDeactivated(IoService service) throws Exception {  
  10.     }  
  11.     public void serviceActivated(IoService service) throws Exception {  
  12.     }  
  13. };  
  14. acceptor.addListener(listener);  
  15. acceptor.removeListener(listener);  

5.1 IoFutureListener    

    还有一种Listener就是IoFutureListener,他在学习笔记四种也出现过,确实也比较简单,能在IoFuture任务完成后被调用,唯一的API:
[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. public interface IoFutureListener<F extends IoFuture> extends EventListener {    
  2.     void operationComplete(F future);    
  3. }    
因为大部分的future都被mina集成在框架里,所以future的listener支持一项特性:若任务已经执行完后添加的listener将被立刻执行。我们先看看future是怎么实现该功能的。
[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. public IoFuture addListener(IoFutureListener<?> listener) {  
  2.     if (listener == null) {  
  3.         throw new IllegalArgumentException("listener");  
  4.     }  
  5.   
  6.     boolean notifyNow = false;  
  7.     synchronized (lock) {  
  8.         if (ready) {  
  9.             notifyNow = true;  
  10.         } else {  
  11.             if (firstListener == null) {  
  12.                 firstListener = listener;  
  13.             } else {  
  14.                 if (otherListeners == null) {  
  15.                     otherListeners = new ArrayList<IoFutureListener<?>>(1);  
  16.                 }  
  17.                 otherListeners.add(listener);  
  18.             }  
  19.         }  
  20.     }  
  21. 是这里了!如果ready完成了,则马上执行  
  22.     if (notifyNow) {  
  23.         notifyListener(listener);  
  24.     }  
  25.     return this;  
  26. }  
那不用说就知道啦,在notiffyListener中将会回调监听器的方法。

6. 结语

    mina框架核心部件基本上都讲的差不多了,看看如果有需要补余的,留在下一章节。
0 0