Scalable Open Financial Architecture
是螞蟻金服自主研發的金融級分布式中間件,包含了建構金融級雲原生架構所需的各個元件,是在金融場景裡錘煉出來的最佳實踐。
本文為《螞蟻金服通信架構 SOFABolt 解析》系列第四篇,作者任展。
《螞蟻金服通信架構 SOFABolt 解析》系列由 SOFA 團隊和源碼愛好者們出品。
SOFARPC:
https://github.com/alipay/sofa-rpcSOFABolt:
https://github.com/alipay/sofa-bolt前言
SOFABolt 是一款基于 Netty 最佳實踐,通用、高效、穩定的通信架構。目前已經運用在了螞蟻中間件的微服務,消息中心,分布式事務,分布式開關,配置中心等衆多産品上。
本文将重點分析 SOFABolt 的連接配接管理功能。
我們知道,一次 tcp 請求大緻分為三個步驟:建立連接配接、通信、關閉連接配接。每次建立新連接配接都會經曆三次握手,中間包含三次網絡傳輸,對于高并發的系統,這是一筆不小的負擔;關閉連接配接同樣如此。為了減少每次網絡調用請求的開銷,對連接配接進行管理、複用,可以極大的提高系統的性能。
下面我們将介紹 SOFABolt 在連接配接管理的實作,包括連接配接生命周期管理、定時斷連及自動重連等。
設計抽象
首先我們将會介紹 SOFABolt 對連接配接的封裝抽象。
1、連接配接封裝
SOFABolt 中定義了一個基礎的連接配接類 -- Connection:
省去 AtributeKey 類型定義以及 Log 配置,以上是Connection中所有的成員變量。包括幾個方面:
連接配接:Channel、Url
版本:protocolCode、version
調用:invokeFutureMap
附着:attributes
引用:referenceCount、id2PoolKey、poolKeys
這裡提一下 protocolCode 和 version,版本資訊會被攜帶至對端,用于連接配接的協商。總的來說,通過對于 Channel 的包裝,Connection 提供了豐富的上下文及引用資訊,是 SOFABolt 連接配接管理的直接對象。
2、連接配接事件
SOFABolt 定義了連接配接事件和事件監聽器用于處理連接配接對象。ConnectionEventType 定義了三種事件類型:CONNECT, CLOSE 和 EXCEPTION. 針對不同的連接配接事件類型,我們可以通過事件監聽器 -- ConnectionEventListener 來進行處理,下面來看一下 ConnectionEventListener 類:
監聽器定義了兩個方法 onEvent 和 addConnectionEventProcessor, 分别是觸發事件和添加事件處理器。整個監聽器采用一個 HashMap 來存儲事件類型及其對應的處理器集合。在觸發相關連接配接事件後,會周遊處理器集合并調用處理器執行。
SOFABolt 的連接配接管理集中在 ConnectionEventHandler 中處理,他繼承了 ChannelDuplexHandler,是标準的用來處理Connection連接配接對象并進行日志列印的一個處理器。先來看一下成員組成:
undefined | left
其中連接配接事件監聽器上文已經提及,剩下的幾個成員從名稱上也通俗易懂,先簡單介紹一下,後續會詳細地展開:
連接配接管理器:管理連接配接對象,包括建立、添加、删除、檢查是否可用等等
連接配接事件監聽器:監聽連接配接事件的觸發,然後執行對應的邏輯
連接配接事件執行器:包裝後的線程池,用于異步觸發連接配接事件監聽器來處理對應的連接配接事件,值得一提的是,這個線程池隻有一個線程。
重連管理器:顧名思義,管理重連的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 對象的心跳打開與關閉
1、建立連接配接
ConnectionFactory 用于建立連接配接對象,SOFABolt 提供了兩個實作類: DefaultConnectionFactory 和 RpcConnectionFactory。這個工廠類執行了用戶端所有 Connection 對象的建立工作,代碼也比較簡單:
注意到了嗎,在建立完畢 Connection 對象後,執行了 fireUserEventTriggered 方法,這樣就保證了每一個 Connection 對象在建立之後都會去觸發 CONNECT 事件。
2、選擇連接配接
ConnectionSelectStrategy 選擇政策的預設實作是随機政策 RandomSelectStrategy, 在執行選擇連接配接時大緻分為兩步:
在開啟CONN_MONITOR_SWITCH監控時,會從該連接配接池所有的連接配接中做一個簡單的filter操作,把CONN_SERVICE_STATUS為ON的連接配接挑選出來,作為選擇池。如果沒有開啟監控,那麼選擇池就是連接配接池。
執行挑選政策,擷取選擇池中的一個連接配接。
3、管理連接配接和連接配接池
管理連接配接和連接配接池是 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 建立了一套完善的連接配接管理機制,從連接配接的建立到選擇再到運作時監控都有着良好的實作。使用自動重連和定時斷連機制,平衡運作時各個連接配接池的數量并且有效地優化資源占用,這些都為它的高性能打下了堅實的基礎。