螞蟻金服通訊框架SOFABolt解析 | 連線管理剖析
SOFA
Scalable Open Financial Architecture
是螞蟻金服自主研發的金融級分散式中介軟體,包含了構建金融級雲原生架構所需的各個元件,是在金融場景裡錘鍊出來的最佳實踐。
本文為《螞蟻金服通訊框架 SOFABolt 解析》系列第四篇,作者任展。
《螞蟻金服通訊框架 SOFABolt 解析》系列由 SOFA 團隊和原始碼愛好者們出品。
SOFARPC: https://github.com/alipay/sofa-rpc
SOFABolt: https://github.com/alipay/sofa-bolt
前言
SOFABolt 是一款基於 Netty 最佳實踐,通用、高效、穩定的通訊框架。目前已經運用在了螞蟻中介軟體的微服務,訊息中心,分散式事務,分散式開關,配置中心等眾多產品上。
本文將重點分析 SOFABolt 的連線管理功能。
我們知道,一次 tcp 請求大致分為三個步驟:建立連線、通訊、關閉連線。每次建立新連線都會經歷三次握手,中間包含三次網路傳輸,對於高併發的系統,這是一筆不小的負擔;關閉連線同樣如此。為了減少每次網路呼叫請求的開銷,對連線進行管理、複用,可以極大的提高系統的效能。
下面我們將介紹 SOFABolt 在連線管理的實現,包括連線生命週期管理、定時斷連及自動重連等。
設計抽象
首先我們將會介紹 SOFABolt 對連線的封裝抽象。
連線封裝
SOFABolt 中定義了一個基礎的連線類 -- Connection
:
省去 AtributeKey 型別定義以及 Log 配置,以上是Connection中所有的成員變數。包括幾個方面:
- 連線:Channel、Url
- 版本:protocolCode、version
- 呼叫:invokeFutureMap
- 附著:attributes
- 引用:referenceCount、id2PoolKey、poolKeys
這裡提一下 protocolCode 和 version,版本資訊會被攜帶至對端,用於連線的協商。總的來說,通過對於 Channel 的包裝,Connection 提供了豐富的上下文及引用資訊,是 SOFABolt 連線管理的直接物件。
連線事件
SOFABolt 定義了連線事件和事件監聽器用於處理連線物件。ConnectionEventType 定義了三種事件型別:CONNECT, CLOSE 和 EXCEPTION. 針對不同的連線事件型別,我們可以通過事件監聽器 -- ConnectionEventListener 來進行處理,下面來看一下 ConnectionEventListener 類:
監聽器定義了兩個方法 onEvent
和 addConnectionEventProcessor
, 分別是觸發事件和新增事件處理器。整個監聽器採用一個 HashMap 來儲存事件型別及其對應的處理器集合。在觸發相關連線事件後,會遍歷處理器集合並呼叫處理器執行。
SOFABolt 的連線管理集中在 ConnectionEventHandler
中處理,他繼承了 ChannelDuplexHandler
,是標準的用來處理Connection連線物件並進行日誌列印的一個處理器。先來看一下成員組成:
其中連線事件監聽器上文已經提及,剩下的幾個成員從名稱上也通俗易懂,先簡單介紹一下,後續會詳細地展開:
- 連線管理器:管理連線物件,包括建立、新增、刪除、檢查是否可用等等
- 連線事件監聽器:監聽連線事件的觸發,然後執行對應的邏輯
- 連線事件執行器:包裝後的執行緒池,用於非同步觸發連線事件監聽器來處理對應的連線事件,值得一提的是,這個執行緒池只有一個執行緒。
- 重連管理器:顧名思義,管理重連的Url物件以及執行重連任務
- 全域性開關:全域性的設定,比如是否需要管理連線物件、是否需要執行重連任務等等
程式碼中方法都比較簡單,大部分的處理邏輯圍繞 Connection 物件展開,主要是維護有關本 Channel 物件的 Connection 物件的生命週期(包括connect、close等事件)。下面著重分析兩個方法:
hannelInactive 方法是在連線斷開前觸發的方法,在 SOFABolt 裡的處理邏輯中,會根據globalSwitch 中 CONN_RECONNECT_SWITCH 的開關狀態來判定是否開啟重連的任務。除此之外,會在最後觸發該 Connection 物件的 CLOSE 事件。這個觸發事件是在非同步執行緒中執行的,也就是上文提到的連線事件執行器。
另一個是 userEventTriggered 方法, 用來觸發自定義的使用者事件,通過檢視本方法的呼叫位置,可以得知,該方法是在連線建立的最初被觸發的,一個簡單的例子可以在RpcServer類中找到:
在連線建立觸發 fireUserEventTriggered 方法後,我們就開始執行對應此方法中的邏輯,也可以看到,在判定是 CONNECT 事件後,通過attr得到繫結在Channel的Connection物件,然後就同
channelInactive 方法一樣,觸發 CONNECT 事件非同步執行對應的處理器邏輯。
連線管理
下面來介紹 ConnectionManager,SOFABolt 提供了預設的實現類 DefaultConnectionManager類。顧名思義,主要負責連線物件的管理:
- 通過工廠建立 Connection 連線物件
- 通過注入的選擇策略進行 Connection 連線的選擇
- 管理建立和新增的 Connection 物件和 ConnectionPool 連線池物件(包括檢查 Connection 物件、維護 ConnectionPool 的健壯性)
- 控制 Connection 物件的心跳開啟與關閉
建立連線
ConnectionFactory 用於建立連線物件,SOFABolt 提供了兩個實現類: DefaultConnectionFactory 和 RpcConnectionFactory。這個工廠類執行了客戶端所有 Connection 物件的建立工作,程式碼也比較簡單:
注意到了嗎,在建立完畢 Connection 物件後,執行了 fireUserEventTriggered 方法,這樣就保證了每一個 Connection 物件在建立之後都會去觸發 CONNECT 事件。
選擇連線
ConnectionSelectStrategy 選擇策略的預設實現是隨機策略 RandomSelectStrategy, 在執行選擇連線時大致分為兩步:
- 在開啟CONN_MONITOR_SWITCH監控時,會從該連線池所有的連線中做一個簡單的filter操作,把CONN_SERVICE_STATUS為ON的連線挑選出來,作為選擇池。如果沒有開啟監控,那麼選擇池就是連線池。
- 執行挑選策略,獲取選擇池中的一個連線。
管理連線和連線池
管理連線和連線池是 ConnectionManager 最主要的作用,用來進行連線和連線池的生命週期管理,包括新增、刪除、檢查健康、恢復連線數等功能。下面先看一個在新增中常見的方法,用來獲取一個連線池物件或者建立一個,限於篇幅,這裡不貼程式碼,有興趣的同學可以在 GitHub 上檢視原始碼。在執行建立連線池物件時,會有兩種邏輯:
- 返回空的連線池
- 返回一個初始化過的連線池(有一定的連線數)
這兩種邏輯其實對應的是兩種需求,第一個對應連線已經建立好然後放入連線池的流程,第二個則是對應通過 Url 來建立一個連線池並且在連線池中做新建連線的流程。那麼對於第二種情況,由於建立連線需要耗時且有可能丟擲異常,所以 ConnectionManager 允許重試兩次。
下面來說說對於連線和連線池的維護方面的功能,大概包含以下幾個方面
- 檢查單個連線的可用性
- 掃描檢查所有連線池裡的連線
- 維護並且修復連線池
ConnectionManager 提供了 check 方法用來檢查單個連線物件是否健康(Channel是否正常、是否活躍、能否寫入)。如果連線失效的話,就會在連線池中刪除該連線,如果連線池為空或者該連線池最後訪問的時間間隔超過了閾值,就會釋放所有連接回收連線池記憶體。
在維護連線池的工作上來說,SOFABolt 主要採用自動重連和定時斷連兩種方式。執行時對連線池的維護十分重要。其一,爆發式呼叫是不穩定因素,如果連線數一旦增多,在峰值流量過去後會產生大量冗餘的連線數;其二,可呼叫的服務往往是會變化的,如果服務不可用那麼我們就需要將這些連線清理掉;因此,對於這兩種情況就需要我們能夠檢查出多餘的連線並且進行釋放,這也就是自動斷連的適用場景。對於重連的情況,則是為了保證整個連線池中連線數量的穩定性,使得在呼叫連線的時候整個QPS是較為穩定的,不會出現很大的波動,這一點也是為了保證通訊的穩定性。定時斷連和自動重連兩者互相平衡,使得連線池中的數量趨於穩定,整個通訊系統也會十分穩定。
自動重連
自動重連機制是通過 GlobalSwitch#CONN_RECONNECT_SWITCH 來控制開閉。具體的重連策略在 ReconnectManager 中實現,它的主要邏輯如下:
- 判斷重連執行緒是否開啟,這主要會考慮到 ReconnectManager 退出邏輯,在ReconnectManager物件銷燬時會中斷重連工作的執行緒
- 判斷時間間隔,因為要控制重連任務的執行速度,所以需要對上一次重連的時間間隔和設定的閾值做比較,這個閾值是1s,如果上一次重連任務的執行速度沒有超過1s,就會Sleep執行緒1s。
- 從重連任務的阻塞佇列中嘗試獲取任務,如果沒有獲取到,執行緒會阻塞。
- 檢查任務是否有效,是否已經取消,如果沒有取消,就會執行重連任務。
- 如果捕捉到異常,不會取消這個重連任務,而是重新將它新增到任務佇列裡。
整個重連任務的新增是在每一次連結斷開的 channelInactive 方法中執行。
定時斷連
定時重連機制是通過 DefaultConnectionMonitor 實現,通過特定的ConnectionMonitorStrategy 來對所有的連結池物件進行監控,內部維護了一個ScheduledThreadPoolExecutor來定時的執行MonitorTask。在 SOFABolt 裡ConnectionMonitorStrategy的實現是ScheduledDisconnectStrategy類,顧名思義,這是一個每次排程會執行關閉連線的監控策略,它的主要邏輯如下:
- 通過filter方法來篩選出服務可用的連線和服務不可用的連線,並儲存在兩個List。
-
管理服務可用的連線,通過閾值 CONNECTION_THRESHOLD 來執行兩種不同的邏輯
- 服務可用的連線數 > CONNECTION_THRESHOLD :接數過多,需要釋放資源,此時就會從這些可用連結裡隨機將一個配置成服務不可用的連線
- 服務的可用連線數 <= CONNECTION_THRESHOLD:連線數尚未佔用過多的資源,只需取出上一次快取在該集合中的“不可用”連結,然後執行closeFreshSelectConnections方法
- 關閉服務不可用的連結
最後
SOFABolt 建立了一套完善的連線管理機制,從連線的建立到選擇再到執行時監控都有著良好的實現。使用自動重連和定時斷連機制,平衡執行時各個連線池的數量並且有效地優化資源佔用,這些都為它的高效能打下了堅實的基礎。
長按關注,獲取分散式架構乾貨
歡迎大家共同打造 SOFAStack https://github.com/alipay