Apache mina2 用户指南(五)过滤器

IoFilter 扮演着很重要角色,它是 MINA 的核心结构之一。它过滤 IoService 和 IoHandler 之间的所有 I/O 事件和请求。如果你有网络应用编程的经验,你完全可以把它当成 Servlet 过滤器的表兄弟。许多开箱即用的过滤器通过使用类似以下的开箱即用过滤器简化横切注入用来提升网络应用的开发速度:
  • LoggingFilter 记录所有事件和请求
  • ProtocolCodecFilter 将一个连入的 ByteBuffer 转化为消息 POJO,反之亦然
  • CompressionFilter 压缩所有数据
  • SSLFilter 添加 SSL - TLS - StartTLS 支持
  • 不止如此!
        本文我们将了解如何为一个真实案例实现一个 IoFilter。通常实现一个 IoFilter 是很简单的,但你也需要 MINA 的内部细节。本文将对这些相关内部细节进行解释。
         现有的过滤器
        我们已经有很多写好的过滤器了。下表列出了所有现有的过滤器,并在他们的用途方面进行简要说明。

过滤器描述
黑名单BlacklistFilter阻止列入黑名单的远程地址的连接
缓冲写BufferedWriteFilter缓存输出请求,就像 BufferedOutputStream 所做的那样
压缩CompressionFilter 
连接限速ConnectionThrottleFilter 
错误生成ErrorGeneratingFilter 
执行者ExecutorFilter 
FileRegion 转换FileRegionWriteFilter 
保持动态KeepAliveFilter 
日志LoggingFilter日志事件消息,比如 MessageReceived、MessageSent、SessionOpened 等等
MDC 注入MdcInjectionFilter将关键 IoSession 属性注入 MDC
等待NoopFilter一个不作任何事情的过滤器。用于测试。
分析器ProfilerTimerFilter分析事件消息,比如 MessageReceived、MessageSent、SessionOpened 等等
协议编解码器ProtocolCodecFilter负责对消息进行编码和解码的过滤器
代理ProxyFilter 
引用计数ReferenceCountingFilter跟踪本过滤器的使用次数
请求响应RequestResponseFilter 
会话属性初始化SessionAttributeInitializingFilter 
写流StreamWriteFilter 
Ssl 过滤器SslFilter 
写请求WriteRequestFilter 


         选择事件重写
        你可以对 IoAdapter 重写以取代直接实现 IoFilter 的做法。除非重写,否则所有接收到的事件将被直接转发给下一个过滤器。
[java]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. public class MyFilter extends IoFilterAdapter {  
  2.     @Override  
  3.     public void sessionOpened(NextFilter nextFilter, IoSession session) throws Exception {  
  4.         // Some logic here...  
  5.         nextFilter.sessionOpened(session);  
  6.         // Some other logic here...  
  7.     }  
  8. }  

         转换写请求
        如果你要通过 IoSession.write() 对一个连入的写请求进行转换,事情可能会变得相当棘手。例如,假设你的过滤器在一个 HighLevelMessage 对象调用 IoSession.write() 时将 HighLevelMessage 转换为 LowLevelMessage。你可以在你的过滤器的 filterWrite() 方法里添加适当的转换代码并认为一切就这样了。但是你也需要注意 messageSent 事件,因为一个 IoHandler 或者任何之后的过滤器会期望 messageSent() 方法以 HighLevelMessage 作为参数调用,因为让调用者在写 HighLevelMessage 的时候被通知到 HighLevelMessage 已发送是不合理的。因此,如果你的过滤器负责转换时你最好同时实现 filterWrite() 和 messageSent()。
        另外还要注意的是,你仍旧需要实现类似的机制,即使输入对象和输出对象的类型是一样的,因为 IoSession.write() 的调用者期望它的 messageSent() 处理器方法有一个具体对象。
        假定你在实现一个将字符串转换为字符数组的过滤器。你的过滤器的 filterWrite() 将会类似于:
[java]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. public void filterWrite(NextFilter nextFilter, IoSession session, WriteRequest request) {  
  2.     nextFilter.filterWrite(  
  3.         session, new DefaultWriteRequest(  
  4.                 ((String) request.getMessage()).toCharArray(), request.getFuture(), request.getDestination()));  
  5. }  

        现在我们需要在 messageSent() 中做相反的事情:
[java]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. public void messageSent(NextFilter nextFilter, IoSession session, Object message) {  
  2.     nextFilter.messageSent(session, new String((char[]) message));  
  3. }  

        字符串到字节缓存的转换怎么样?这样我们会更加高效,我们不在需要重建原始消息 (字符串)。但是,这比前面的例子复杂:
[java]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. public void filterWrite(NextFilter nextFilter, IoSession session, WriteRequest request) {  
  2.     String m = (String) request.getMessage();  
  3.     ByteBuffer newBuffer = new MyByteBuffer(m, ByteBuffer.wrap(m.getBytes());  
  4.   
  5.     nextFilter.filterWrite(  
  6.             session, new WriteRequest(newBuffer, request.getFuture(), request.getDestination()));  
  7. }  
  8.   
  9. public void messageSent(NextFilter nextFilter, IoSession session, Object message) {  
  10.     if (message instanceof MyByteBuffer) {  
  11.         nextFilter.messageSent(session, ((MyByteBuffer) message).originalValue);  
  12.     } else {  
  13.         nextFilter.messageSent(session, message);  
  14.     }  
  15. }  
  16.   
  17. private static class MyByteBuffer extends ByteBufferProxy {  
  18.     private final Object originalValue;  
  19.     private MyByteBuffer(Object originalValue, ByteBuffer encodedValue) {  
  20.         super(encodedValue);  
  21.         this.originalValue = originalValue;  
  22.     }  
  23. }  

        如果你使用的是 MINA 2.0,这将跟 1.0 和 1.1 有所不同。同时也参考一下  CompressionFilter  和  RequestResponseFilter
         过滤 sessionCreated 事件时须谨慎
        sessionCreated 是一个特殊事件,它必须在 I/O 处理程序中执行 (参考 线程模型的配置)。决不允许将 sessionCreated 事件转发给其他线程。
[java]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. public void sessionCreated(NextFilter nextFilter, IoSession session) throws Exception {  
  2.     // ...  
  3.     nextFilter.sessionCreated(session);  
  4. }  
  5.   
  6. // DON'T DO THIS!  
  7. public void sessionCreated(final NextFilter nextFilter, final IoSession session) throws Exception {  
  8.     Executor executor = ...;  
  9.     executor.execute(new Runnable() {  
  10.         nextFilter.sessionCreated(session);  
  11.         });  
  12.     }  

         提防空缓存!
        在一些案例中 MINA 使用了一个空的缓冲区作为一个内部信号。空缓存有时会成为一个问题,因为它可能会造成各种各样的异常,比如 IndexOutOfBoundsException。这里我们介绍如何避免类似于这种难以预料的情况。
        ProtocolCodecFilter 使用了一个空缓存 (比如 buf.hasRemaining() = 0) 来标记消息的结束部分。如果你的过滤器放在 ProtocolCodecFilter 之前,如果你的过滤器实现在缓存为空时能抛出一个异常的话,请确认你的过滤器将空缓存转发给了下一个过滤器:
[java]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. public void messageSent(NextFilter nextFilter, IoSession session, Object message) {  
  2.     if (message instanceof ByteBuffer && !((ByteBuffer) message).hasRemaining()) {  
  3.         nextFilter.messageSent(nextFilter, session, message);  
  4.         return;  
  5.     }  
  6.     ...  
  7. }  
  8.   
  9. public void filterWrite(NextFilter nextFilter, IoSession session, WriteRequest request) {  
  10.     Object message = request.getMessage();  
  11.     if (message instanceof ByteBuffer && !((ByteBuffer) message).hasRemaining()) {  
  12.         nextFilter.filterWrite(nextFilter, session, request);  
  13.         return;  
  14.     }  
  15.     ...  
  16. }  

        这样的话,我们是否总是要为每个过滤器插入 if 块?幸运的是,不需要。这是处理空缓存的黄金法则:
  • 如果你的过滤器及时在缓存为空时也能正常工作,你就完全不需要添加 if 块了
  • 如果你的过滤器放在 ProtocolCodecFilter 之后,你也不需要添加 if 块
  • 除此之外的话你就需要 if 块了
        如果你需要加 if 块,记着你不总是需要遵循上面例子所讲的。你可以在任何地方检查缓存是否为空,只要你的过滤器不会抛出异常。 译者注:之所以如此这般谨慎处理,是因为一旦在过滤器中抛出异常,当前线程即消亡,负责该项业务处理的逻辑将会出席问题,因为后续过滤器无法对其进行继续处理。
原文链接: http://mina.apache.org/mina-project/userguide/ch5-filters/ch5-filters.html
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值