在使用三方客戶端進行網絡通信時,我們首先要確定客戶端SDK是否是基於連接池技術實現的。我們知道,TCP是面向連接的基於字節流的協議:
- 面向連接,意味着連接需要先創建再使用,創建連接的三次握手有一定開銷;
- 基於字節流,意味着字節是發送數據的最小單元,TCP協議本身無法區分哪幾個字節是完整的消息體,也無法感知是否有多個客戶端在使
用同一個TCP連接,TCP只是一個讀寫數據的管道
如果客戶端SDK沒有使用連接池,而直接是TCP連接,那麼就需要考慮每次建立TCP連接(3次握手)的開銷,並且因爲TCP基於字節流,在多線程的情況下對同一連接進行復用,可能會產生線程安全問題
我們先看一下涉及TCP連接的客戶端SDK,對外提供API的三種方式。在面對各種三方客戶端的時候,只有先識別出其屬於哪一種,才能理清楚
使用方式。
1,連接池和連接分離的API:有一個XXXPool類負責連接池實現,先從其獲得連接XXXConnection,然後用獲得的連接進行服務端請求,
完成後使用者需要歸還連接。通常,XXXPool是線程安全的,可以併發獲取和歸還連接,而XXXConnection是非線程安全的。對應到連
接池的結構示意圖中,XXXPool就是右邊連接池那個框,左邊的客戶端是我們自己的代碼。
2,內部帶有連接池的API:對外提供一個XXXClient類,通過這個類可以直接進行服務端請求;這個類內部維護了連接池,SDK使用者無需
考慮連接的獲取和歸還問題。一般而言,XXXClient是線程安全的。對應到連接池的結構示意圖中,整個API就是藍色框包裹的部分。
3,非連接池的API:一般命名爲XXXConnection,以區分其是基於連接池還是單連接的,而不建議命名爲XXXClient或直接是XXX。直接連
接方式的API基於單一連接,每次使用都需要創建和斷開連接,性能一般,且通常不是線程安全的。對應到連接池的結構示意圖中,這種
形式相當於沒有右邊連接池那個框,客戶端直接連接服務端創建連接。
1,如果是分離方式,那麼連接池本身一般是線程安全的,可以複用。每次使用需要從連接池獲取連接,使用後歸還,歸還的工作由使用者負
責。
2,如果是內置連接池,SDK會負責連接的獲取和歸還,使用的時候直接複用客戶端。
3,如果SDK沒有實現連接池(大多數中間件、數據庫的客戶端SDK都會支持連接池),那通常不是線程安全的,而且短連接的方式性能不會
很高,使用的時候需要考慮是否自己封裝一個連接池。
Jedis的API實現是我們說的三種類型中的第一種,也就是連接池和連接分離的API, JedisPool是線程安全的連接池,Jedis是非線程安全的單一連接。
而Apache HttpClient是內置連接池的API。
**連接池的配置不是一成不變的**
連接池提供了許多參數,包括最小(閒置)連接、最大連接、閒置連接生存時間、連接生存時間等。 其中,最重要的參數是最大連接數,它決定了連接池能使用的連接數量上限,達到上限後,新來的請求需要等待其他請求釋放連接。但最大連接數不是設置得越大越好。如果設置得太大,不僅僅是客戶端需要耗費過多的資源維護連接,更重要的是由於服務端對應的是多個客戶端,每一個客戶端都保持大量的連接,會給服務端帶來更大的壓力。這個壓力又不僅僅是內存力,可以想一下如果服務端的網絡模型是
一個TCP連接一個線程,那麼幾千個連接意味着幾千個線程,如此多的線程會造成大量的線程切換開銷。
當然,連接池最大連接數設置得太小,很可能會因爲獲取連接的等待時間太長,導致吞吐量低下,甚至超時無法獲取連接