Mina文件 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塊,請記住你並不總是需要按照上面的例子。只要過濾器沒有引發意外異常,您就可以檢查緩衝區是否為空。