Mina Basics 05-过滤器
IoFilter是MINA核心构造之一,起着非常重要的作用。它过滤IoService和IoHandler之间的所有I / O事件和请求。如果您有Web应用程序编程经验,可以放心地认为它是Servlet过滤器的表兄弟。提供了许多开箱即用的过滤器,通过使用开箱即用的过滤器简化典型的横切关注点来加速网络应用程序开发速度,例如:
1.LoggingFilter记录所有事件和请求。
2.ProtocolCodecFilter将传入的ByteBuffer转换为消息POJO,反之亦然。
3.CompressionFilter压缩所有数据。
4.SSLFilter添加SSL - TLS - StartTLS支持。
5.还有很多!
在本教程中,我们将介绍如何为现实世界的用例实现IoFilter。一般来说,实现IoFilter很容易,但您可能还需要了解MINA内部的细节。这里将解释任何相关的内部属性。
1.已实现的过滤器
2.有选择地覆盖事件
3.转换写请求
4.过滤sessionCreated事件时要小心
5.注意空缓冲!
已实现的过滤器
我们已经编写了许多过滤器。下表列出了所有现有过滤器,并简要说明了其用法。
过滤器 |
类 |
描述 |
Blacklist |
阻止列入黑名单的远程地址的连接 |
|
Buffered Write |
像BufferedOutputStream一样缓存传出请求 |
|
Compression |
|
|
ConnectionThrottle |
|
|
ErrorGenerating |
|
|
Executor |
|
|
FileRegionWrite |
|
|
KeepAlive |
|
|
Logging |
记录事件消息,如MessageReceived,MessageSent,SessionOpened,.... |
|
MDC Injection |
将关键IoSession属性注入MDC |
|
Noop |
一个什么都不做的过滤器。对测试有用。 |
|
Profiler |
配置文件事件消息,如MessageReceived,MessageSent,SessionOpened,... |
|
ProtocolCodec |
负责编码和解码消息的过滤器 |
|
Proxy |
|
|
Reference counting |
跟踪此过滤器的使用次数 |
|
RequestResponse |
|
|
SessionAttributeInitializing |
|
|
StreamWrite |
|
|
SslFilter |
|
|
WriteRequest |
|
有选择地覆盖事件
您可以扩展IoAdapter而不是直接实现IoFilter。除非重写,否则任何收到的事件都将立即转发到下一个过滤器:
public class MyFilter extends IoFilterAdapter { @Override public void sessionOpened(NextFilter nextFilter, IoSession session) throws Exception { // Some logic here... nextFilter.sessionOpened(session); // Some other logic here... } }
转换写请求
如果你要通过IoSession.write()转换传入的写请求,事情会变得相当棘手。例如,假设当使用HighLevelMessage对象调用IoSession.write()时,您的过滤器会将HighLevelMessage转换为LowLevelMessage。您可以在过滤器的filterWrite()方法中插入适当的转换代码,并认为这就是全部。但是,您必须注意,您还需要处理messageSent事件,因为IoHandler或您旁边的任何过滤器都希望使用HighLevelMessage作为参数调用messageSent()方法,因为调用者收到LowLevelMessage的通知是不合理的。当调用者实际编写HighLevelMessage时发送。因此,如果过滤器执行转换,则必须同时实现filterWrite()和messageSent()。
还请注意,即使输入对象和输出对象的类型相同(例如CompressionFilter),您仍然需要实现类似的机制,因为IoSession.write()的调用者将完全期望他在他或她的messageSent中写入的内容(处理方法。
假设您正在实现一个将String转换为char []的过滤器。您的过滤器的filterWrite()将如下所示:
public void filterWrite(NextFilter nextFilter, IoSession session, WriteRequest request) { nextFilter.filterWrite( session, new DefaultWriteRequest( ((String) request.getMessage()).toCharArray(), request.getFuture(), request.getDestination())); }
现在,我们需要在messageSent()中执行相反的操作:
public void messageSent(NextFilter nextFilter, IoSession session, Object message) { nextFilter.messageSent(session, new String((char[]) message)); }
String-to-ByteBuffer转换怎么样?我们可以更有效率,因为我们不需要重建原始消息(String)。但是,它比前一个例子稍微复杂一些:
public void filterWrite(NextFilter nextFilter, IoSession session, WriteRequest request) { String m = (String) request.getMessage(); ByteBuffer newBuffer = new MyByteBuffer(m, ByteBuffer.wrap(m.getBytes()); nextFilter.filterWrite( session, new WriteRequest(newBuffer, request.getFuture(), request.getDestination())); } public void messageSent(NextFilter nextFilter, IoSession session, Object message) { if (message instanceof MyByteBuffer) { nextFilter.messageSent(session, ((MyByteBuffer) message).originalValue); } else { nextFilter.messageSent(session, message); } } private static class MyByteBuffer extends ByteBufferProxy { private final Object originalValue; private MyByteBuffer(Object originalValue, ByteBuffer encodedValue) { super(encodedValue); this.originalValue = originalValue; } }
如果您使用的是MINA 2.0,则会与1.0和1.1略有不同。请同时参考CompressionFilter和RequestResponseFilter。
过滤sessionCreated事件时要小心
sessionCreated是一个必须在I / O处理器线程中执行的特殊事件(请参阅配置线程模型)。永远不要将sessionCreated事件转发给其他线程
public void sessionCreated(NextFilter nextFilter, IoSession session) throws Exception { // ... nextFilter.sessionCreated(session); } // 不要这样做 public void sessionCreated(final NextFilter nextFilter, final IoSession session) throws Exception { Executor executor = ...; executor.execute(new Runnable() { nextFilter.sessionCreated(session); }); }
注意空缓冲区!
在几种情况下,MINA使用空缓冲区作为内部信号。空缓冲区有时会成为问题,因为它是各种异常的原因,例如IndexOutOfBoundsException。本节介绍如何避免这种意外情况。
ProtocolCodecFilter使用空缓冲区(即buf.hasRemaining()= 0)来标记消息的结尾。如果您的过滤器放在ProtocolCodecFilter之前,请确保您的过滤器将空缓冲区转发到下一个过滤器,如果缓冲区为空,则过滤器实现会抛出意外异常:
public void messageSent(NextFilter nextFilter, IoSession session, Object message) { if (message instanceof ByteBuffer && !((ByteBuffer) message).hasRemaining()) { nextFilter.messageSent(nextFilter, session, message); return; } ... } public void filterWrite(NextFilter nextFilter, IoSession session, WriteRequest request) { Object message = request.getMessage(); if (message instanceof ByteBuffer && !((ByteBuffer) message).hasRemaining()) { nextFilter.filterWrite(nextFilter, session, request); return; } ... }
我们是否总是必须为每个过滤器插入if块?幸运的是,你不必。这是处理空缓冲区的黄金法则:
1.如果您的过滤器即使缓冲区为空也没有任何问题,您根本不需要添加if块。
2.如果过滤器放在ProtocolCodecFilter之后,则根本不需要添加if块。
3,.否则,您需要if块。
如果你需要if块,请记住你并不总是需要按照上面的例子。只要过滤器没有引发意外异常,您就可以检查缓冲区是否为空。