原文地址:Mina过滤器(Mina user guide Chapter5 Filter)
IoFilter是Mina核心结构提供的一个很重要的角色。它过滤了所有在IoService和IoHandler之间的I / O事件和请求。如果你有一个使用web应用程序编程的经验,你可以放心地认为这是一个类似的Servlet过滤器。Mina提供了许多开箱即用的过滤器加速网络应用程序开发的速度,通过简化使用开箱即用过滤器等典型的横切关注点:
在本教程中,我们将介绍如何实现一个现实世界的IoFilter用例。一般很容易实现一个IoFilter,但你可能还需要知道Mina内部的细节。任何相关的内部属性将被解释。
我们已经创建了许多的过滤器,下面用表格列举一下已经存在的过滤器,并附上简单的使用说明:
Filter |
class |
描述 |
Blacklist |
BlacklistFilter |
块连接远程地址黑名单过滤器 |
Buffered Write |
BufferedWriteFilter |
用BufferedOutputStream做缓冲输出请求
|
Compression |
CompressionFilter |
|
ConnectionThrottle |
ConnectionThrottleFilter |
|
ErrorGenerating |
ErrorGeneratingFilter |
|
Executor |
ExecutorFilter |
|
FileRegionWrite |
FileRegionWriteFilter |
|
KeepAlive |
KeepAliveFilter |
|
Logging |
LoggingFilter |
日志记录事件消息,如:MessageReceived,MessageSent, SessionOpened, |
MDC Injection |
MdcInjectionFilter |
将IoSession 属性注入MDC |
Noop |
NoopFilter |
一个什么也不做的过滤器,只对测试有用 |
Profiler |
ProfilerTimerFilter |
剖析事件消息,如: MessageReceived, MessageSent, SessionOpened, ... |
ProtocolCodec |
ProtocolCodecFilter |
编解码过滤器 |
Proxy |
ProxyFilter |
|
Reference counting |
ReferenceCountingFilter |
跟踪数字用法的过滤器 |
RequestResponse |
RequestResponseFilter |
|
SessionAttributeInitializing |
SessionAttributeInitializingFilter |
|
StreamWrite |
StreamWriteFilter |
|
SslFilter |
SslFilter |
|
WriteRequest |
WriteRequestFilter |
|
您可以扩展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到 LowLevelMessage进行过滤,IoSession.write()被 HighLevelMessage对象所调用。你可以插入适当的转换代码过滤的filterWrite()方法,并认为这样就够了。然而,你必须注意,您还需要照顾messageSent事件,因为IoHandler或任何过滤器接下来会期望messageSent()将HighLevelMessage作为方法的参数。由于它是非理性的,当调用者HighLevelMessage准备通知LowLevelMessage,实际上LowLevelMessage已发送完成。因此,如果你的过滤器执行转换,你必须实现filterWrite()和messageSent()。
仍需注意,您还需要执行类似的机制,即使输入和输出对象的类型是相同的(例如CompressionFilter)。因为IoSession.write()的调用者将期望他通过messageSent()写入到对应的handler方法。
假设你正在实施一个过滤器,将一个字符串转换为一个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转换呢?我们可以更有效率,因为我们不需要重建原始消息(字符串)。然而,这是比之前更复杂的例子:
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;
}}
如果您使用的是Mina2.0,他与1.0和1.1有些不同。请同时参考CompressionFilter和RequestResponseFilter。
sessionCreated是一个特殊的事件,必须在I / O处理器线程中执行(参见配置线程模型)。sessionCreated从不转发事件给其他线程。
public void sessionCreated(NextFilter nextFilter, IoSession session) throws Exception {
// ...
nextFilter.sessionCreated(session);}
// DON'T DO THIS!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块吗?幸运的是,你不需要。处理空缓冲区的黄金法则:
如果你需要if块,请记住你不总是需要遵循上面的例子。您可以检查是否缓冲区为空,无论你想怎样都行,只要你的过滤器不抛出一个意想不到的异常。