Grizzly FilterChains and Filters

原文鏈接:https://javaee.github.io/grizzly/filterchainfilters.html#

FilterChains and Filters

在前面的部分中,我們提到了Processor,它的角色-處理髮生在Grizzly * Connection * s上的 I/O 事件。
所述FilterChain是 Grizzly 中使用的最有用的類型的處理器。

filterchain diagram

根據名稱所描述的那樣,FilterChain是 Filters 的鏈。每個Filter代表要執行的處理工作的一個單元,
其目的是檢查和/或修改由FilterChainContext表示的事務的狀態。

爲了讓您瞭解FilterChain的外觀,下面是FilterChain的示例,該示例實現了HTTP服務器邏輯:
http-filterchain

  • TransportFilter 負責從網絡連接讀取數據到 Buffer,並將數據從緩衝區寫入到網絡連接
  • HttpFilter 負責 Buffer <-> HttpPacket 轉換(雙向)
  • HttpServerFilter負責處理請求 HttpPackets 並生成響應 HttpPackets,
    並將它們以相反的方向(HttpServerFilter-> HttpFilter-> TransportFilter)發送回FilterChain。

那麼,如果我們要實現HTTPS服務器呢?這很簡單:
https-filterchain
我們僅添加一個SSLFilter,負責對SSL安全數據進行編碼/解碼。

如我們所見,在處理任何I / O事件期間,FilterChain中的過濾器將按特定順序執行。
重要的是要記住,除了WRITE事件外,大多數 I/O事件都是從第一個過濾器到最後一個處理(從上面的模式從左到右)處理的,
而WRITE事件的處理是從鏈中的最後一個過濾器開始到第一個(從右到左)處理的。上面的模式)。

讓我們定義一些術語以使以下描述更清楚:

  • Upstream 上游, 從此過濾器到鏈中最後一個過濾器的方向(在以上架構中從左到右);
  • Downstream 下游,從此過濾器到鏈中第一個過濾器的方向(在上述架構中從右到左);

讓我們看一下 FilterChain 可以處理哪些 I/O 事件,爲此,我們可以看一下 Filter 接口方法:

public NextAction handleRead(FilterChainContext ctx) throws IOException;

public NextAction handleWrite(FilterChainContext ctx) throws IOException;

public NextAction handleConnect(FilterChainContext ctx) throws IOException;

public NextAction handleAccept(FilterChainContext ctx) throws IOException;

public NextAction handleClose(FilterChainContext ctx) throws IOException;

所以 I/O 事件有

  • READ:可以從連接(Connection)中獲得數據,可以對其進行讀取和處理;
  • WRITE: 數據將被寫入到連接(Connection)中,並且Filter可能負責轉換數據表示形式,例如HttpPacket- > 上面模式中的Buffer;
  • CONNECT:新客戶端連接已連接;
  • ACCEPT(僅限TCP):新的客戶端連接已被服務器接受連接(TCPNIOServerConnection);
  • CLOSE:連接已關閉(本地或對端);

重要的是要記住,特定連接上的相同 I/O 事件是串行處理的。例如,如果我們在連接 “A” 上處理 READ I/O 事件,
則在上一個 READ I/O 事件的處理完成之前,Grizzly 將永遠不會開始在同一連接 “A” 上處理另一個 READ I/O 事件。
如果用戶決定擁有 I/O 事件處理的所有權,則仍應遵守串行事件處理的"規則"。

另外,FilterChain 篩選器(filters) 能夠啓動和處理自定義事件通知。事件發起者可以選擇通過FilterChain在上游或下游發出事件,例如:

public NextAction handleRead(FilterChainContext ctx) throws IOException {
        // Here we decide to notify downstream Filters 這裏我們決定通知下游過濾器 
        ctx.notifyDownstream(new OneEvent(...));

        // Notify upstream Filters 通知上游過濾器 
        ctx.notifyUpstream(new AnotherEvent(...));
}

FilterChain 中的過濾器(Filters) 可以通過實現以下方法來攔截和處理自定義事件:

public NextAction handleEvent(FilterChainContext ctx, FilterChainEvent event) throws IOException;

如我們所見,每個 Filter “handle” 方法都有 FilterChainContext 參數,並返回 NextAction 結果。
filterChain-context

FilterChainContext表示一個上下文(狀態)Context(status),與特定 Connection 上特定 I/O 事件的處理相關聯,
因此其生命週期綁定到單個I / O事件的處理。

FilterChainContext包含以下狀態信息:

Connection

發生 Connection I/O 事件;

Address

對等地址。在大多數情況下,它返回與 Connection.getPeerAddress() 相同的值,
除非在我們處理未綁定 UDP 連接上的 READ 事件的情況下。在這種情況下,FilterChainContext.getAddress()
將返回發送數據的對等方的地址;

Message

正在處理的消息。這是過濾器在 I/O 事件處理期間可能更改的唯一值。通常在傳入/傳出消息解析/序列化期間使用它。
每個過濾器都能夠獲取初始消息數據,將其轉換爲不同的表示形式,將其放回原處,並將處理傳遞給鏈中的下一個過濾器。

例如,當處理 READ 事件時,HttpFilter從FilterChainContext獲取消息作爲Grizzly Buffer,將其轉換爲HttpPacket,
將 HttpPacket 設置回 FilterChainContext 消息,然後將控制權傳遞給 HttpServerFilter,
後者將從 FilterChainContext 獲取 HttpPacket 並對其進行處理。

除了保持狀態外,FilterChainContext還支持常用的 I/O 操作:

Read

ReadResult readResult = ctx.read();

此操作從該過濾器(不包括)上游的鏈中的第一個過濾器(包括)開始執行阻塞的FilterChain讀取。
當對 READ I/O 事件的處理將到達當前 Filter 時,該操作將返回結果,並且 FilterChain 將要調用此 Filter的handleRead(…)操作。

Write

ctx.write(message);

// or 

ctx.write(message, completionHandler)

// or

ctx.write(address, message, completionHandler); //Unbound UDP only

此操作從第一個過濾器(含)之後的此過濾器(不含)開始執行無阻塞 FilterChain 寫入。
此操作從此 Filter(獨佔)開始在 FilterChain 上啓動 WRITE I/O 事件的處理。

Flush

ctx.flush();
//or
ctx.flush(completionHandler);

此操作初始化並通知下游篩選器(filters)有關特殊的 TransportFilter.FlushEvent 的信息,
以便每個篩選器都能夠處理此事件,並確保所有緩存的數據均已寫入Connection。

Event notification

ctx.notifyUpstream(event);
// or
ctx.notifyDownstream(event);

該操作將特定FilterChainEvent通知給FilterChain中的所有上游/下游Filter 。

NextAction

如前所述,在處理 I/O 事件期間,除了WRITE事件(從最後一個Filter到first進行處理)外,
FilterChain 從頭到尾依次調用Filters。同時,過濾器可以通過返回不同類型的 NextAction 來更改默認的 I/O 事件處理順序:

StopAction

return ctx.getStopAction();

指示FilterChain停止處理此I / O事件。通常,如果沒有足夠的數據來繼續FilterChain處理,或者它是鏈中的最後一個Filter,則返回StopAction。

StopAction可以參數化:

return ctx.getStopAction(incompleteChunk);
// or
return ctx.getStopAction(incompleteChunk, appender);

StopAction中的incompleteChunk意味着沒有足夠的數據來繼續FilterChain處理。隨着更多數據可用,但在FilterChain調用Filter之前,它將檢查Filter在上一次調用之後是否存儲了任何數據。如果存在一個不完整的塊,它將新數據追加到存儲的數據中,並將結果作爲FilterChainContext消息傳遞。

注意:incompleteChunk應該是“可附加的”,因此FilterChain將知道如何將新的數據塊附加到存儲的塊中。因此,incompleteChunk應該實現org.glassfish.grizzly.Appendable或org.glassfish.grizzly.Appender作爲附加參數。

InvokeAction

return ctx.getInvokeAction();

指示FilterChain根據自然執行順序在鏈中運行下一個Filter。
可以使用 incompleteChunk 參數創建InvokeAction :

return ctx.getInvokeAction(incompleteChunk, appender);

這會指示FilterChain存儲incompleteChunk並像執行非參數化版本一樣繼續執行FilterChain。

此功能對於從源緩衝區解析一條消息或幾條消息並發現存在剩餘數據量不足以轉換爲應用程序消息的情況特別有用。因此,
開發人員可以繼續使用已解析的消息進行FilterChain處理,並存儲incompleteChunk餘數。隨着更多數據可用,
但在FilterChain再次調用Filter之前,它將檢查Filter在上一次調用之後是否存儲了任何數據。
如果存在一個不完整的塊,它將新數據追加到存儲的數據中,並將結果作爲FilterChainContext消息傳遞。

注意:incompleteChunk應該是“可附加的”,因此FilterChain將知道如何將新的數據塊附加到存儲的塊中。
因此,incompleteChunk應該實現org.glassfish.grizzly.Appendable或org.glassfish.grizzly.Appender作爲附加參數。

另一種選擇是創建具有的InvokeAction unparsedChunk 參數:

return ctx.getInvokeAction(unparsedChunk);

這指示FilterChain存儲unparsedChunk並像執行非參數化版本一樣繼續執行FilterChain。與上述“ incompleteChunk”情況不同,
這次我們不知道unparsedChunk是否具有足夠的數據可轉換爲應用程序消息。一旦FilterChain執行完成,將恢復鏈中最新的Filter的unparsedChunk,將立即從存儲了unparededChunk的Filter開始重新初始化FilterChain處理。

對於從源緩衝區解析消息並發現緩衝區包含剩餘部分(可能包含也可能不包含更多消息)的情況,此功能特別有用。
這使開發人員可以提取第一條消息,並在當前消息處理完成後保存其餘要處理的消息。

RerunFilterAction

return ctx.getRerunFilterAction();

指示 FilterChain 重新運行一次此 Filter。這對於簡化 I/O 事件處理代碼並避免遞歸很有用。

SuspendAction

return ctx.getSuspendAction();

指示 FilterChain 終止(離開)當前線程中的 I/O 事件處理。用戶將可以通過調用來恢復 I/O 事件處理.

  • ctx.resume(): 在暫停的同一過濾器上恢復處理。
  • ctx.resume(NextAction): 在被暫停的相同Filter上恢復處理,但不將控制權傳遞給Filter-它模擬Filter的處理完成,就像它返回NextAction作爲結果一樣
  • ctx.resumeNext(): 在掛起的過濾器旁邊的過濾器處恢復處理。 和 ctx.resume(ctx.getInvokeAction()) 相同.

請注意,在返回 SuspendAction 之後,在繼續 I/O 事件處理之前,Grizzly 將不會在同一Connection上初始化相同的 I/O 事件處理。
例如,如果我們在 READ 事件處理期間返回 SuspendAction,則 Grizzly 不會通知 FilterChain 有關同一 Connection 上的任何新數據的通知,
直到已暫停事件的 READ 事件完成爲止。

ForkAction (was SuspendStopAction)

return txt.getForkAction();

這個動作是非常相似的 SuspendAction,除了一件重要的事情。獲取 ForkAction 之後,Grizzly將繼續監聽Connection上的相同I / O事件,並在發生時通知FilterChain。
使用NextAction時要格外小心,以確保兩個或多個線程不會同時處理同一I / O操作。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章