在程式設計的世界裡,經常會遇到連接配接池,那連接配接池到底是什麼呢?
什麼是池?
池,一種資源抽象的形象化說法。程式設計世界中的池是一組資源, 可以随時使用, 但不随時地建立和釋放。資源池(resource pool)被認為是一種設計模式,這裡的資源主要是指系統資源, 這些資源不專屬于某個程序或内部資源。用戶端向池請求資源, 并使用傳回的資源進行指定的操作。當用戶端使用完資源後, 會把資源放回池中而不是釋放或丢棄掉。
任何技術都有自己的應用邊界,池作為一種資源使用技術,典型的使用情形是:
當擷取資源的成本較高的時候
當請求資源的頻率很高且使用資源總數較低的時候
當面對性能問題,涉及到處理時間延遲的時候
池中的資源主要有兩類:需要系統調用(system call) 的系統資源,或主演需要網絡通信的遠端資源, 如資料庫連接配接、套接字連接配接、線程和記憶體配置設定等等。池中的資源一般不包括像字型庫或圖檔等大的資料對象, 那些資源的存儲一般是通過是資料緩存或資料庫技術實作的。由于資源池的存在, 從池中擷取資源所需的時間變成了可預知的,進而在一定程度上解決性能的問題。
根據資源的類型,資源池一般包括連接配接池、線程池和記憶體池。
連接配接池
連接配接池是建立和管理一個網絡連接配接資源池的技術,這些連接配接一般預先準備好被任何需要它們的線程或者程序使用。網絡連接配接根據連接配接的生命周期可以粗略的分為兩種:長連結和短連結。就web應用而言,短連接配接就是一般的http請求,長連接配接如websocket。
短連結适合大部分應用。對于遠端方法的執行時間遠大于連接配接建立時間(看網絡情況大約為數毫秒)的時候,其連接配接建立時間可以被忽略,此時短連接配接政策基本不會有較大性能損失。另外,對于非頻繁調用火災對延遲時間不敏感的服務也适合使用短連接配接政策。
對于高并發或者高吞吐量的應用,網絡連接配接的建立消耗是很大的,對于這種應用應該使用長連接配接政策的連接配接池實作。

在各種連接配接池的實作中,常用的參數一般有:連接配接數相關,連接配接時間相關,有效性相關。
連接配接數
設計一個連接配接池,要确定池中的連接配接數量,包括最小空閑連接配接數,最大空閑連接配接數,連接配接池最大持有連接配接數。當然連接配接數可以變化,動态縮放,确定每次增加/減少的連接配接數量。
連接配接的有效性
保證連接配接池中的連接配接有效性,相當于增加了連接配接心跳的檢測。同時,還有從池中擷取用戶端接口時的有效性,将用戶端接口歸還連接配接池時的有效性,當配置或實作了相關的管理服務,可以通過管理工具觀察連接配接池的使用情況。例如對于Java的應用,如果配置了JMX服務的話,可以通過JMX管理工具觀察Java連接配接池的狀态。
連接配接有效性測試可以減少長連接配接失效造成的遠端調用失敗,對于那些對連接配接失效而造成的調用失敗很敏感的服務,可以開啟各種合适的連接配接有效性測試政策來保障所取得的用戶端是連接配接正常的。
時間相關參數
為了保持池中連接配接的有效性,空閑連接配接檢測時間也就是心跳間隔,這往往取決于業務使用連接配接池的場景。另外,還有從連接配接池中擷取連接配接的最大等待時間,一般地預設為-1,即無可用連接配接會抛出異常,當設為0時表示無窮大。
網絡通信連接配接池
網絡通信的連接配接池主要節省建立TCP連接配接的時間,進而降低了請求的總處理時間。用戶端為每個服務端執行個體維護一個連接配接池。如果連接配接池中有空閑連接配接,則複用這個連接配接。如果連接配接池中沒有空閑連接配接,則會建立一個新的TCP連接配接或者等待池中出現空閑的連接配接。
當用戶端使用池中連接配接處理完一個請求時,如果連接配接池中的空閑連接配接數小于連接配接池的大小,則将目前使用的連接配接放入連接配接池。 如果連接配接池中的空閑連接配接數大于等于連接配接池的大小,則關閉目前使用的連接配接。
面向http短連接配接的連接配接池,服務端支援keepalive時才有效,如果服務端關閉keepalive,則效果等同于短連接配接,就沒有連接配接池的作用了。同理,如果連接配接池的大小設定為0,也等同于短連接配接的方式。服務端支援Keepalive的時候,可以減少CPU和記憶體的使用,允許請求和應答的HTTP管道化,減少了後續請求的延遲,報告錯誤也無需關閉TCP連接配接。
一般地,對于延遲敏感的業務,可以使用連接配接池機制。
資料庫連接配接池
開頭的例子是一個資料庫連接配接池。資料庫連接配接池也可以了解為維護資料庫連接配接的緩存, 以便在需要對資料庫的請求時可以重用連接配接。
為每個使用者打開和維護資料庫連接配接需要消耗大量的資源,而資料庫連接配接池用于提高資料庫中執行指令的性能,減少了使用者必須等待的時間。在資料庫連接配接池中, 建立連接配接後将其放入池中, 再次使用, 不必重建立立新的連接配接。如果所有的連接配接都被使用, 則建立新的連接配接并被添加到池中。
基于 web 的應用程式和企業應用程式一般都使用應用伺服器來處理連接配接池。當頁面需要通路資料庫時, 隻需使用池中的現有連接配接, 并且隻在池中沒有空閑連接配接的情況下建立新連接配接。這減少了連接配接到資料庫響應單個請求的開銷,需要頻繁通路資料庫的本地應用程式也可以從資料庫連接配接池中受益。一些庫不僅實作了資料庫連接配接池還實作了相關的 SQL 查詢池, 簡化了資料庫操作密集型應中連接配接池的實作。Java中常用的資料庫連接配接池有:DBCP 、C3P0、BoneCP、Proxool、DBPool、XAPool、Primrose、SmartPool、MiniConnectionPoolManager及Druid等。
通過對連接配接池進行配置, 對最小連接配接、最大連接配接和空閑連接配接的數量加以限制, 可以優化在特定場景和特定環境中資料庫連接配接池的性能。
端上的連接配接池
由于網際網路尤其是廣域網中的速度非可控性,特别是移動網際網路(基于3G/4G)的速度的不确定性,在端上的應用也将連接配接池作為一種重要的技術手段。
以Chrome浏覽器為例,其網絡庫采取連接配接池的方式管理連接配接的建立、配置設定以及釋放,當請求可以直接從連接配接池中擷取複用連接配接時,可以減少建立連接配接的時間消耗。除了websoket連接配接池之外,包含三種類型的連接配接池:
TransportClientSocketPool
SSLClientSocketPool
SOCKSClientSocketPool
其中TransportClientSocketPool為低層連接配接池,SSLClientSocketPool和SOCKSClientSocketPool為高層連接配接池,高層連接配接池包含低層連接配接池或其他高層連接配接池的對象,這三種連接配接池類可以組合出多種連接配接池對象。打開chrome://net-internals/#sockets 可以看到浏覽器目前的連接配接狀态。
在app中,連接配接池同樣被廣泛采用,主流的網絡通信庫都支援連接配接池,例如Okhttp。平台層也是如此,例如Android 平台中的binder 連接配接池。
線程池
在計算機程式設計中, 線程池是實作計算機程式中并發執行的軟體設計方式。線程池維護多個線程, 等待監督程式為并發執行配置設定任務。通過維護一個線程池, 可以提高性能, 避免執行延遲。可用線程的數量取決于程式可用的計算資源, 如并行處理器、核心、記憶體和網絡套接字。
一個常見的線程執行任務排程方法是同步隊列, 稱為任務隊列。池中的線程将等待任務從隊列中移除, 并在執行完成後将其放置到已完成的任務隊列中。線程池的大小是為執行任務而保留的線程數,通常是一個可調參數, 調整它可以以優化程式性能。
線程池對于為每個任務建立一個新線程的主要好處是線程建立和銷毀開銷僅限于初始建立池, 這可能導緻更好的性能和更好的系統穩定性。通常情況下,建立和銷毀一個線程及其相關資源是一個費時的過程。然而, 池中的線程數量過多, 會浪費記憶體, 并且在可運作的線程之間切換上下文也可能會引發性能問題。一個socket連接配接到另一個網絡主機, 可能需要許多 CPU 周期, 可以将socket與在多個網絡事務中使用的線程聯系起來, 可以更有效地維護它。
根據等待任務的數量, 可以在應用程式的生存期間動态調整線程數。例如, 如果許多網頁同時送出請求的時候, web 伺服器可以添加線程, 當請求逐漸減少時可以删除線程。
線程池使用中需要注意的問題:
建立太多的線程會浪費資源
關注建立了但未使用的線程
銷毀了大量線程後又花費較多的時間來重新建立它們
建立線程過于緩慢可能導緻用戶端性能變差
銷毀線程過于緩慢可能會餓死其他的處理流程
記憶體池
記憶體池, 是使用池來進行記憶體管理, 使動态記憶體配置設定時達到 malloc 或者 new 的效果。由于記憶體碎片的存在,一個有效的方案是預先配置設定一些記憶體大小相同的記憶體塊,許多實時作業系統都适用了記憶體池。一種簡單的記憶體池實作如下圖所示:
對于記憶體池的應用而言,可以通過以下方式配置設定、通路和釋放記憶體:
從池中配置設定記憶體時,函數将确定所需塊的池。如果該池的所有區塊已被保留,則該函數試圖在下一個較大的池中找到一個。配置設定的記憶體塊用句柄表示
擷取配置設定記憶體的通路指針
釋放以前配置設定的記憶體塊
記憶體池将句柄劃分為池索引、記憶體塊索引以及版本, 進而在内部解釋句柄。池和記憶體塊索引允許使用句柄快速通路對應的塊, 而在每個新配置設定中增量的版本允許檢測已經釋放記憶體塊的句柄。
記憶體池允許使用恒定的執行時間來配置設定記憶體。數千個對象在池中的記憶體釋放隻是一個操作, 而不是一個一個的Free。記憶體池也可以采用樹狀結構, 應用于特殊的程式設計行為, 如循環,遞歸等。固定大小的塊記憶體池不需要為每個塊配置設定中繼資料存儲, 不需要描述配置設定塊的大小等特性。
記憶體池還可用于對象, 在這種情況下,對象本身沒有外部資源, 隻占用記憶體, 已經建立了的對象避免了對象建立時的記憶體配置設定。當對象建立成本較高時, 對象池是有用的, 但在某些情況下, 這種簡單的對象池可能并不有效, 實際上還可能會降低性能。
轉載
原文位址:
http://blog.csdn.net/wireless_com/article/details/79072305 作者:半吊子全棧工匠 來源:CSDN