第 6 章 ChannelHandler 相關

2021-08-20 07:01:01 字數 3931 閱讀 6399

channelhandler 作為程式邏輯的核心部分,其地位是不可估量的。接下來我們將對它以及與它相關的一些元件進行詳細的描述。

首先我們看一下如下這張圖,對 channelhandler 相關元件之間的關係有乙個比較直觀的認識

從上圖我們對 channel 相關元件的關係有了乙個大概的認識。接下我們對每乙個進行詳細說明

對於每乙個新建立的 channel ,一定且只會被分配乙個 channelpipeline。這種一 一 對應的關係在 channel 的生命週期內都是存在的。乙個 channelpipeline 由多個 channelhandler 組成,它就等價於乙個 channelhandler 例項鏈。如下圖,展示了乙個典型同時具有入站和出站 channelhandler 的 channelpipeline 布局

在 netty 中總是將 channelpipeline 的入站口——最先讀取資料的處理器(上圖中的左側:1號)作為頭部,而將出站口——最先寫入資料的處理器(上圖中的最右側:5號)作為尾部。

從上圖還可以得知:入站 channelhandler 和 出站 channelhandler 可以同時新增到 channelpipeline 中,channelhandler 的排序與它們的新增次序有關。當乙個入站事件在 channelpipeline 中傳播時,他會「自動」跳過處理出站事件的相關 channelhandler(也就是不相關的 handler,如上圖中的3、5);對於出站事件類似。這裡有個問題

如何實現自動跳過不想關的處理器的?

其實如果簡單從**實現層面來理解,需要兩步走:

1. 確定該 handler 是出站還是入站 handler

由於入站 handler 和 出站 handler 傳輸資料使用的方法不一樣,那自定義 handler(selfhandler)只需要覆蓋 channelhandleradapter 中不同的方法即可判斷該 selfhandler 是出站還是入站事件的 handler。例如,若 selfhandler 覆蓋了 

channelhandleradapter 中 channelread(channelhandlercontext ctx, object msg) 方法,則為入站 handler;如 

selfhandler 覆蓋了 

channelhandleradapter 中 write(channelhandlercontext ctx, object msg, channelpromise promise) 方法,則為出站 handler;

若 selfhandler 同時覆蓋了channelread 和 write 兩個方法,則 selfhandler 是同時可以處理出、入站事件的 handler。

2. 通過 @skip 來跳過對應的 handler

如果確定好 selfchannelhandler 是出站 handler 的時候,當上圖中的 2 號 入站 handler 傳遞訊息到 3 號 selfchannelhandler (出站 handler)的時候,由於 selfchannelhandler 沒有實現對應的 channelread 方法,所以會走 channelhandleradapter 的 預設實現方法,該預設實現方法有一 註解 @skip(跳過),顧名思義,跳過該 handler。

通過呼叫 channelpipeline 的相關方法,我們可以靈活的控制 channelhandler 在channelpipeline 的位置,從而可以控制業務邏輯。常用方法如下

addfirst()、addbefore()、addafter()、 addlast()、 remove()、replace()。

總結說來

channelhandlercontext 是 channelpipeline 和 channelhandler 之間的一種關聯,主要功能就是管理它所關聯的 channelhandler 與 同乙個 channelpipeline 中的 其他 channelhandler 之間的互動。

每當向 channelpipeline 中新增 channelhandler 時,都會建立乙個 channelhandlercontext 。在 channelpipeline、channel、channelhandlercontext 三個勒種介面中有些共同的方法。三個不同的介面呼叫同樣乙個方法來處理事件的時候,會有所不同:channelpipeline、和channel呼叫的處理事件的方法,該事件會沿著整個 channelpipeline 傳播,channelhandlercontext 呼叫同樣的方法的時候,事件從下乙個能處理該事件的 channelhandler 開始傳播的。

針對 channelhandlercontext ,有以下兩大特性:

channelhandlercontext 的方法會產生更短的事件流,應該盡可能的利用這個特性來獲得效能的最大提公升

channelhandlercontext 和 channelhandler 之間的關聯(繫結)是永遠不會變的,所以快取它的引用是安全的

在 netty 程式中,我們通過入站 handler 處理完資料之後,一般會在最後收到事件流的入站 handler 處理完邏輯後,往遠端寫乙個響應訊息。如下圖

從圖中可以看到事件a 最後來到 4 號入站 hander,處理完之後,在 4 號 handler 中寫入以響應事件 b,寫入的方法有三種

1. 通過 channel 寫入:channel.write(事件 b )

事件流向為:4 號 ---> 5號 --->3 號 --->遠端

2. 通過 channelpipeline 寫入:pipeline.write(事件 b)

事件流向為:4 號 ---> 5號 --->3 號 --->遠端

3. 通過 channelhandlercontext 寫入:channelhandlercontext.write(事件 b )

事件流向為:4 號 --->3 號 --->遠端

channelhandler 之間的事件流傳遞都是通過 channelhandlercontext 來實現的,從**層面來看,其實就是呼叫了 channelhandlercontext 的 fire***()方法來觸發下乙個 channelhandler 的 ***()方法。

1. 使用 @sharable 標記共享 channelhandler

在多個 channelpipeline 中安裝同乙個 channelhandler時,需要在該 channelhandler 上用 @sharable 註解標記。在使用共享 channelhandler 的時候尤其要注意是否執行緒安全。其用法如下:

@sharable

public class sharablehandler extends channelhandleradapter

共享 channelhandler 的目的在於統計一些共有的資訊(比如系統訪問次數)。

對於出站異常,我們只需要繼承 exceptioncaught 方法,在裡面完成相應的異常處理邏輯即可

對於異常,通常的做法是:對呼叫給定的方法返回的 channelfuture 新增乙個監聽,待操作完成後,得到通知。典型做法如下

channelfuture future = channel.write(msg);

future.addlistener(new channelfuturelistener())

}}

本章主要學習了事件是如何在 channelhandler 之間進行傳遞的,以及對於一些異常的處理。下一章節講學習 netty 的 eventloop 和 併發模型

第 6章 函式

6.1.2引數 2.引數陣列 c 允許為函式指定乙個 只能乙個 特殊的引數,這個引數必須是函式定義中的最後乙個引數,可用params關鍵字定義他們 如 params int vals 3.引用引數和值引數 理解 將本來在函式中引數按值引用的規則改變成按傳遞引用,使得這個引數會改變,定義引數和傳遞引數...

第6章 函式

1.自動物件 只存在於塊執行期間的物件 2.區域性靜態物件static 在程式執行路徑第一次經過物件定義語句時初始化,並且知道程式終止才被銷毀,如果區域性靜態變數沒有顯示的初始值,初始化為0.3.如果函式無須改變引用形參的值,最好將其生命為常量引用。4.使用引用形參返回額外資訊 5.和其他初始化過程...

第6章 小結

在這一章,你學習了如何為應用程式新增儲存層。一開始,使用儲存和恢復例項狀態處理函式來在會話期間儲存 activity 的例項資料,之後,學習了 sharedpreference 你可以使用它在程式的元件間儲存例項的值和使用者的設定。android 為所有的應用程式提供了完整的 sqlite rdbm...