您好,登錄后才能下訂單哦!
這篇文章主要講解了“GRPC連接池如何實現”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“GRPC連接池如何實現”吧!
在分布式高并發服務器中,client到server以及server中的多個節點之間的連接往往使用連接池來管理。簡單來說就是將提前創建好的連接保存在池中,當有請求到來時,直接使用連接池中的連接對server端訪問,省去了創建連接和銷毀連接的開銷(TCP建立連接時的三次握手和釋放連接時的四次揮手),從而提高了性能。
連接池的擴縮容
空閑連接的超時與保活
池滿的處理機制
通常連接池屬性包含最大空閑連接數和最大活躍連接數。
最大空閑連接數
:連接池一直保持的連接數,無論這些連接被使用與否都會被保持。如果客戶端對連接池的使用量不大,便會造成服務端連接資源的浪費。
最大活躍連接數
:連接池最多保持的連接數,如果客戶端請求超過次數,便要根據池滿的處理機制
來處理沒有得到連接的請求。
擴容
:當請求到來時,如果連接池中沒有空閑的連接,同時連接數也沒有達到最大活躍連接數,便會按照特定的增長策略創建新的連接服務該請求,同時用完之后歸還到池中,而不是關閉連接。
縮容
:當連接池一段時間沒有被使用,同時池中的連接數超過了最大空閑連接數,那么便會關閉一部分連接,使池中的連接數始終維持在最大空閑連接數。
超時
如果連接沒有被客戶端使用的話,便會成為空閑連接,在一段時間后,服務端可能會根據自己的超時策略關閉空閑連接,此時空閑連接已經失效,如果客戶端再使用失效的連接,便會通信失敗。為了避免這種情況發生,通常連接池中的連接設有最大空閑超時時間
(最好略小于服務器的空閑連接超時時間),在從池中獲取連接時,判斷是否空閑超時,如果超時則關閉,沒有超時則可以繼續使用。
保活
如果服務器發生重啟,那么連接池中的連接便會全部失效,如果此時再從池中獲取連接,不論獲取到哪一個,都將通信失敗。因此,連接池必須考慮連接的保活問題,有兩種解決方法:
1、連接池設置一個Ping函數,專門用來做連接的保活。在從池中獲取連接的時候,Ping一下服務器,如果得到響應,則連接依然有效,便可繼續使用,如果超時無響應,則關閉該連接,生成新的連接,由于每次都要Ping一下,必然會增加延遲。也可以后臺用一個線程或者協程定期的執行Ping函數,進行連接的保活,缺點是感知連接的失效會有一定的延遲,從池中仍然有可能獲取到失效的連接。
2、客戶端加入相應的重試機制。比如重試3次,前兩次從池中獲取連接執行,如果報的錯是失效的連接等有關連接問題的錯誤,那么第3次從池中獲取的時候帶上參數,指定獲取新建的連接,同時連接池移除前兩次獲取的失效的連接。
連接池不可能無限的容納連接,當池滿時,有兩種處理機制:
1、池新建連接,并返回給客戶端,當客戶端用完時,如果池滿則關閉連接,否則放入池中。
2、設置一定的超時時間來等待空閑連接。需要客戶端加入重試機制,避免因超時之后獲取不到空閑連接產生的錯誤。
服務啟動時建立連接池。
初始化連接池,建立最大空閑連接數個連接。
請求到來時,從池中獲取一個連接。如果沒有空閑連接且連接數沒有達到最大活躍連接數,則新建連接;如果達到最大活躍連接數,設置一定的超時時間,等待獲取空閑連接。
獲取到連接后進行通信服務。
釋放連接,此時是將連接放回連接池,如果池滿則關閉連接。
釋放連接池,關閉所有連接。
關于GRPC的介紹,不在這里闡述,可閱讀深入了解GRPC協議,也可自行Google。這里主要簡要說明GRPC的兩個特性:多路復用、超時重連。
多路復用
GRPC使用HTTP/2作為應用層的傳輸協議,HTTP/2會復用底層的TCP連接。每一次RPC調用會產生一個新的Stream,每個Stream包含多個Frame,Frame是HTTP/2里面最小的數據傳輸單位。同時每個Stream有唯一的ID標識,如果是客戶端創建的則ID是奇數,服務端創建的ID則是偶數。如果一條連接上的ID使用完了,Client會新建一條連接,Server也會給Client發送一個GOAWAY Frame強制讓Client新建一條連接。一條GRPC連接允許并發的發送和接收多個Stream,而控制的參數便是MaxConcurrentStreams
,Golang的服務端默認是100。
超時重連
我們在通過調用Dial或者DialContext函數創建連接時,默認只是返回ClientConn結構體指針,同時會啟動一個Goroutine異步的去建立連接。如果想要等連接建立完再返回,可以指定grpc.WithBlock()傳入Options來實現。超時機制很簡單,在調用的時候傳入一個timeout的context就可以了。重連機制通過啟動一個Goroutine異步的去建立連接實現的,可以避免服務器因為連接空閑時間過長關閉連接、服務器重啟等造成的客戶端連接失效問題。也就是說通過GRPC的重連機制可以完美的解決連接池設計原則中的空閑連接的超時與保活問題。
以Golang的GRPC客戶端為例:
GRPC默認的參數對于傳輸大數據塊來說不夠友好,我們需要進行特定參數的調優。
MaxSendMsgSize
GRPC最大允許發送的字節數,默認4MiB,如果超過了GRPC會報錯。Client和Server我們都調到4GiB。
MaxRecvMsgSize
GRPC最大允許接收的字節數,默認4MiB,如果超過了GRPC會報錯。Client和Server我們都調到4GiB。
InitialWindowSize
基于Stream的滑動窗口,類似于TCP的滑動窗口,用來做流控,默認64KiB,吞吐量上不去,Client和Server我們調到1GiB。
InitialConnWindowSize
基于Connection的滑動窗口,默認16 * 64KiB,吞吐量上不去,Client和Server我們也都調到1GiB。
KeepAliveTime
每隔KeepAliveTime時間,發送PING幀測量最小往返時間,確定空閑連接是否仍然有效,我們設置為10S。
KeepAliveTimeout
超過KeepAliveTimeout,關閉連接,我們設置為3S。
PermitWithoutStream
如果為true,當連接空閑時仍然發送PING幀監測,如果為false,則不發送忽略。我們設置為true。
代碼:https://github.com/shimingyah/pool
基于GRPC的多路復用、超時重連特性,我們很容易實現GRPC連接池。
提供簡潔的Pool和Conn的接口設計。
GRPC是支持多路復用的,所以在設計GRPC池的時候和其他連接池區別之一是支持連接復用,通過MaxConcurrentStreams
控制,默認64。我們稱單個的GRPC為物理連接
,復用的連接為邏輯連接
。池的實際有效連接邏輯連接
=物理連接
* MaxConcurrentStreams
。
擴容
初始化池的有效連接數(邏輯連接)為:最大空閑連接數
* MaxConcurrentStreams
,每一次請求都會對池的引用計數原子++,同時hash求取選取連接,當引用計數超過邏輯連接數時,就需要進行擴容了,如果最大空閑連接沒有達到最大活躍連接數,則按照double的方式擴容,如果達到了最大活躍連接數,我們會根據Reuse參數的值來做進一步操作:如果為true,則繼續使用池中的連接,即使用的是物理連接的邏輯連接,關閉連接時,對引用計數原子--即可,如果為false,則新建連接,關閉連接時還需要對連接進行真正的Close。
縮容
如果池的引用計數為0時,便會觸發縮容操作,是連接維持到最大空閑連接數。
基于GRPC的Keepalived特性,我們不需要自己實現保活機制,也無需關注連接池中的連接是否有效,因為就算失效,GRPC會自動重連的,此時只不過耗時會略微增加,即認為除了服務器一直處于down狀態等原因,連接池中的連接是始終有效的。
由于使用hash求余,每個GRPC上并發的Stream可能會超過MaxConcurrentStreams。
不同場景對應的連接池配置也不一樣,需要根據自己的場景壓測得出連接池的最佳參數配置。
感謝各位的閱讀,以上就是“GRPC連接池如何實現”的內容了,經過本文的學習后,相信大家對GRPC連接池如何實現這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。