新聞中心
如果將互聯(lián)網(wǎng)應(yīng)用比喻成沖浪的話, 可能需要先學(xué)會在“池”中游泳。

創(chuàng)新互聯(lián)-專業(yè)網(wǎng)站定制、快速模板網(wǎng)站建設(shè)、高性價(jià)比平利網(wǎng)站開發(fā)、企業(yè)建站全套包干低至880元,成熟完善的模板庫,直接使用。一站式平利網(wǎng)站制作公司更省心,省錢,快速模板網(wǎng)站建設(shè)找我們,業(yè)務(wù)覆蓋平利地區(qū)。費(fèi)用合理售后完善,10年實(shí)體公司更值得信賴。
引子
AI賦能萬物,老碼農(nóng)的伙伴們也曾經(jīng)開發(fā)了一個(gè)基于圖數(shù)據(jù)庫的知識問答系統(tǒng),在壓力測試的時(shí)候發(fā)現(xiàn)隨著并發(fā)數(shù)的增加,響應(yīng)的時(shí)延明顯變長,看時(shí)延分布,是應(yīng)用程序與圖數(shù)據(jù)庫之間的交互時(shí)延過長。結(jié)構(gòu)不做調(diào)整,優(yōu)化圖數(shù)據(jù)庫后,發(fā)現(xiàn)在并發(fā)量上來之后,效果仍不明顯。
看代碼,觀察ELK中的日志,發(fā)現(xiàn)了問題所在————高并發(fā)時(shí)連接的創(chuàng)建時(shí)間較長。時(shí)間所限,替換為httpclient的連接池,post 和 get都采用池中的連接,性能問題迎刃而解。
在編程的世界里,經(jīng)常會遇到連接池,那連接池到底是什么呢?
什么是池
池,一種資源抽象的形象化說法。編程世界中的池是一組資源, 可以隨時(shí)使用, 但不隨時(shí)地創(chuàng)建和釋放。
資源池(resource pool)被認(rèn)為是一種設(shè)計(jì)模式,這里的資源主要是指系統(tǒng)資源, 這些資源不專屬于某個(gè)進(jìn)程或內(nèi)部資源??蛻舳讼虺卣埱筚Y源, 并使用返回的資源進(jìn)行指定的操作。當(dāng)客戶端使用完資源后, 會把資源放回池中而不是釋放或丟棄掉。
任何技術(shù)都有自己的應(yīng)用邊界,池作為一種資源使用技術(shù),典型的使用情形是:
- 當(dāng)獲取資源的成本較高的時(shí)候
- 當(dāng)請求資源的頻率很高且使用資源總數(shù)較低的時(shí)候
- 當(dāng)面對性能問題,涉及到處理時(shí)間延遲的時(shí)候
池中的資源主要有兩類:需要系統(tǒng)調(diào)用(system call) 的系統(tǒng)資源,或主演需要網(wǎng)絡(luò)通信的遠(yuǎn)程資源, 如數(shù)據(jù)庫連接、套接字連接、線程和內(nèi)存分配等等。池中的資源一般不包括像字體庫或圖片等大的數(shù)據(jù)對象, 那些資源的存儲一般是通過是數(shù)據(jù)緩存或數(shù)據(jù)庫技術(shù)實(shí)現(xiàn)的。由于資源池的存在, 從池中獲取資源所需的時(shí)間變成了可預(yù)知的,從而在一定程度上解決性能的問題。
根據(jù)資源的類型,資源池一般包括連接池、線程池和內(nèi)存池。
連接池
連接池是創(chuàng)建和管理一個(gè)網(wǎng)絡(luò)連接資源池的技術(shù),這些連接一般預(yù)先準(zhǔn)備好被任何需要它們的線程或者進(jìn)程使用。
網(wǎng)絡(luò)連接根據(jù)連接的生命周期可以粗略的分為兩種:長鏈接和短鏈接。就web應(yīng)用而言,短連接就是一般的http請求,長連接如websocket。
短鏈接適合大部分應(yīng)用。對于遠(yuǎn)程方法的執(zhí)行時(shí)間遠(yuǎn)大于連接創(chuàng)建時(shí)間(看網(wǎng)絡(luò)情況大約為數(shù)毫秒)的時(shí)候,其連接創(chuàng)建時(shí)間可以被忽略,此時(shí)短連接策略基本不會有較大性能損失。另外,對于非頻繁調(diào)用火災(zāi)對延遲時(shí)間不敏感的服務(wù)也適合使用短連接策略。
對于高并發(fā)或者高吞吐量的應(yīng)用,網(wǎng)絡(luò)連接的創(chuàng)建消耗是很大的,對于這種應(yīng)用應(yīng)該使用長連接策略的連接池實(shí)現(xiàn)。
連接池中的幾個(gè)常用參數(shù)
在各種連接池的實(shí)現(xiàn)中,常用的參數(shù)一般有:連接數(shù)相關(guān),連接時(shí)間相關(guān),有效性相關(guān)。
連接數(shù)
設(shè)計(jì)一個(gè)連接池,要確定池中的連接數(shù)量,包括最小空閑連接數(shù),***空閑連接數(shù),連接池***持有連接數(shù)。當(dāng)然連接數(shù)可以變化,動態(tài)縮放,確定每次增加/減少的連接數(shù)量。
連接的有效性
保證連接池中的連接有效性,相當(dāng)于增加了連接心跳的檢測。同時(shí),還有從池中獲取客戶端接口時(shí)的有效性,將客戶端接口歸還連接池時(shí)的有效性,當(dāng)配置或?qū)崿F(xiàn)了相關(guān)的管理服務(wù),可以通過管理工具觀察連接池的使用情況。例如對于Java的應(yīng)用,如果配置了JMX服務(wù)的話,可以通過JMX管理工具觀察Java連接池的狀態(tài)。
連接有效性測試可以減少長連接失效造成的遠(yuǎn)程調(diào)用失敗,對于那些對連接失效而造成的調(diào)用失敗很敏感的服務(wù),可以開啟各種合適的連接有效性測試策略來保障所取得的客戶端是連接正常的。
時(shí)間相關(guān)參數(shù)
為了保持池中連接的有效性,空閑連接檢測時(shí)間也就是心跳間隔,這往往取決于業(yè)務(wù)使用連接池的場景。另外,還有從連接池中獲取連接的***等待時(shí)間,一般地默認(rèn)為-1,即無可用連接會拋出異常,當(dāng)設(shè)為0時(shí)表示無窮大。
網(wǎng)絡(luò)通信連接池
網(wǎng)絡(luò)通信的連接池主要節(jié)省創(chuàng)建TCP連接的時(shí)間,從而降低了請求的總處理時(shí)間??蛻舳藶槊總€(gè)服務(wù)端實(shí)例維護(hù)一個(gè)連接池。如果連接池中有空閑連接,則復(fù)用這個(gè)連接。如果連接池中沒有空閑連接,則會建立一個(gè)新的TCP連接或者等待池中出現(xiàn)空閑的連接。
當(dāng)客戶端使用池中連接處理完一個(gè)請求時(shí),如果連接池中的空閑連接數(shù)小于連接池的大小,則將當(dāng)前使用的連接放入連接池。 如果連接池中的空閑連接數(shù)大于等于連接池的大小,則關(guān)閉當(dāng)前使用的連接。
面向http短連接的連接池,服務(wù)端支持keepalive時(shí)才有效,如果服務(wù)端關(guān)閉keepalive,則效果等同于短連接,就沒有連接池的作用了。
同理,如果連接池的大小設(shè)置為0,也等同于短連接的方式。服務(wù)端支持Keepalive的時(shí)候,可以減少CPU和內(nèi)存的使用,允許請求和應(yīng)答的HTTP管道化,減少了后續(xù)請求的延遲,報(bào)告錯(cuò)誤也無需關(guān)閉TCP連接。
一般地,對于延遲敏感的業(yè)務(wù),可以使用連接池機(jī)制。
數(shù)據(jù)庫連接池
開頭的例子是一個(gè)數(shù)據(jù)庫連接池。數(shù)據(jù)庫連接池也可以理解為維護(hù)數(shù)據(jù)庫連接的緩存, 以便在需要對數(shù)據(jù)庫的請求時(shí)可以重用連接。
為每個(gè)用戶打開和維護(hù)數(shù)據(jù)庫連接需要消耗大量的資源,而數(shù)據(jù)庫連接池用于提高數(shù)據(jù)庫中執(zhí)行命令的性能,減少了用戶必須等待的時(shí)間。在數(shù)據(jù)庫連接池中, 創(chuàng)建連接后將其放入池中, 再次使用, 不必重新建立新的連接。如果所有的連接都被使用, 則創(chuàng)建新的連接并被添加到池中。
基于 web 的應(yīng)用程序和企業(yè)應(yīng)用程序一般都使用應(yīng)用服務(wù)器來處理連接池。當(dāng)頁面需要訪問數(shù)據(jù)庫時(shí), 只需使用池中的現(xiàn)有連接, 并且只在池中沒有空閑連接的情況下建立新連接。這減少了連接到數(shù)據(jù)庫響應(yīng)單個(gè)請求的開銷,需要頻繁訪問數(shù)據(jù)庫的本地應(yīng)用程序也可以從數(shù)據(jù)庫連接池中受益。
一些庫不僅實(shí)現(xiàn)了數(shù)據(jù)庫連接池還實(shí)現(xiàn)了相關(guān)的 SQL 查詢池, 簡化了數(shù)據(jù)庫操作密集型應(yīng)中連接池的實(shí)現(xiàn)。Java中常用的數(shù)據(jù)庫連接池有:DBCP 、C3P0、BoneCP、Proxool、DBPool、XAPool、Primrose、SmartPool、MiniConnectionPoolManager及Druid等。
通過對連接池進(jìn)行配置, 對最小連接、***連接和空閑連接的數(shù)量加以限制, 可以優(yōu)化在特定場景和特定環(huán)境中數(shù)據(jù)庫連接池的性能。
端上的連接池
由于互聯(lián)網(wǎng)尤其是廣域網(wǎng)中的速度非可控性,特別是移動互聯(lián)網(wǎng)(基于3G/4G)的速度的不確定性,在端上的應(yīng)用也將連接池作為一種重要的技術(shù)手段。
以Chrome瀏覽器為例,其網(wǎng)絡(luò)庫采取連接池的方式管理連接的建立、分配以及釋放,當(dāng)請求可以直接從連接池中獲取復(fù)用連接時(shí),可以減少建立連接的時(shí)間消耗。除了websoket連接池之外,包含三種類型的連接池:
- TransportClientSocketPool
- SSLClientSocketPool
- SOCKSClientSocketPool
其中TransportClientSocketPool為低層連接池,SSLClientSocketPool和SOCKSClientSocketPool為高層連接池,高層連接池包含低層連接池或其他高層連接池的對象,這三種連接池類可以組合出多種連接池對象。打開chrome://net-internals/#sockets 可以看到瀏覽器當(dāng)前的連接狀態(tài)。
在app中,連接池同樣被廣泛采用,主流的網(wǎng)絡(luò)通信庫都支持連接池,例如Okhttp。平臺層也是如此,例如Android 平臺中的binder 連接池。
線程池
在計(jì)算機(jī)編程中, 線程池是實(shí)現(xiàn)計(jì)算機(jī)程序中并發(fā)執(zhí)行的軟件設(shè)計(jì)方式。線程池維護(hù)多個(gè)線程, 等待監(jiān)督程序?yàn)椴l(fā)執(zhí)行分配任務(wù)。通過維護(hù)一個(gè)線程池, 可以提高性能, 避免執(zhí)行延遲。可用線程的數(shù)量取決于程序可用的計(jì)算資源, 如并行處理器、核心、內(nèi)存和網(wǎng)絡(luò)套接字。
一個(gè)常見的線程執(zhí)行任務(wù)調(diào)度方法是同步隊(duì)列, 稱為任務(wù)隊(duì)列。池中的線程將等待任務(wù)從隊(duì)列中移除, 并在執(zhí)行完成后將其放置到已完成的任務(wù)隊(duì)列中。線程池的大小是為執(zhí)行任務(wù)而保留的線程數(shù),通常是一個(gè)可調(diào)參數(shù), 調(diào)整它可以以優(yōu)化程序性能。
線程池對于為每個(gè)任務(wù)創(chuàng)建一個(gè)新線程的主要好處是線程創(chuàng)建和銷毀開銷僅限于初始創(chuàng)建池, 這可能導(dǎo)致更好的性能和更好的系統(tǒng)穩(wěn)定性。通常情況下,創(chuàng)建和銷毀一個(gè)線程及其相關(guān)資源是一個(gè)費(fèi)時(shí)的過程。
然而, 池中的線程數(shù)量過多, 會浪費(fèi)內(nèi)存, 并且在可運(yùn)行的線程之間切換上下文也可能會引發(fā)性能問題。一個(gè)socket連接到另一個(gè)網(wǎng)絡(luò)主機(jī), 可能需要許多 CPU 周期, 可以將socket與在多個(gè)網(wǎng)絡(luò)事務(wù)中使用的線程聯(lián)系起來, 可以更有效地維護(hù)它。
根據(jù)等待任務(wù)的數(shù)量, 可以在應(yīng)用程序的生存期間動態(tài)調(diào)整線程數(shù)。例如, 如果許多網(wǎng)頁同時(shí)發(fā)出請求的時(shí)候, web 服務(wù)器可以添加線程, 當(dāng)請求逐漸減少時(shí)可以刪除線程。
線程池使用中需要注意的問題:
- 創(chuàng)建太多的線程會浪費(fèi)資源
- 關(guān)注創(chuàng)建了但未使用的線程
- 銷毀了大量線程后又化費(fèi)較多的時(shí)間來重新創(chuàng)建它們
- 創(chuàng)建線程過于緩慢可能導(dǎo)致客戶端性能變差
- 銷毀線程過于緩慢可能會餓死其他的處理流程
內(nèi)存池
內(nèi)存池, 是使用池來進(jìn)行內(nèi)存管理, 使動態(tài)內(nèi)存分配時(shí)達(dá)到 malloc 或者 new 的效果。由于內(nèi)存碎片的存在,一個(gè)有效的方案是預(yù)先分配一些內(nèi)存大小相同的內(nèi)存塊,許多實(shí)時(shí)操作系統(tǒng)都適用了內(nèi)存池。一種簡單的內(nèi)存池實(shí)現(xiàn)如下圖所示:
對于內(nèi)存池的應(yīng)用而言,可以通過以下方式分配、訪問和釋放內(nèi)存:
- 從池中分配內(nèi)存時(shí),函數(shù)將確定所需塊的池。如果該池的所有區(qū)塊已被保留,則該函數(shù)試圖在下一個(gè)較大的池中找到一個(gè)。分配的內(nèi)存塊用句柄表示。
- 獲取分配內(nèi)存的訪問指針
- 釋放以前分配的內(nèi)存塊
內(nèi)存池將句柄劃分為池索引、內(nèi)存塊索引以及版本, 從而在內(nèi)部解釋句柄。池和內(nèi)存塊索引允許使用句柄快速訪問對應(yīng)的塊, 而在每個(gè)新分配中增量的版本允許檢測已經(jīng)釋放內(nèi)存塊的句柄。
內(nèi)存池允許使用恒定的執(zhí)行時(shí)間來分配內(nèi)存。數(shù)千個(gè)對象在池中的內(nèi)存釋放只是一個(gè)操作, 而不是一個(gè)一個(gè)的Free。內(nèi)存池也可以采用樹狀結(jié)構(gòu), 應(yīng)用于特殊的編程行為, 如循環(huán),遞歸等。固定大小的塊內(nèi)存池不需要為每個(gè)塊分配元數(shù)據(jù)存儲, 不需要描述分配塊的大小等特性。
內(nèi)存池還可用于對象, 在這種情況下,對象本身沒有外部資源, 只占用內(nèi)存, 已經(jīng)創(chuàng)建了的對象避免了對象創(chuàng)建時(shí)的內(nèi)存分配。當(dāng)對象創(chuàng)建成本較高時(shí), 對象池是有用的, 但在某些情況下, 這種簡單的對象池可能并不有效, 實(shí)際上還可能會降低性能。
小結(jié)
池是一種資源共享和復(fù)用的技術(shù),把管理的理念引入到編程世界中。從基礎(chǔ)的內(nèi)存池,到線程池,再到各種連接池,根據(jù)應(yīng)用場景還可以繼續(xù)細(xì)分,如句柄池,緩存池.....幾乎涵蓋了互聯(lián)網(wǎng)應(yīng)用的大部分角落。如果將互聯(lián)網(wǎng)成沖浪的話, 可能需要先學(xué)會在池中游泳吧。
【本文來自專欄作者“老曹”的原創(chuàng)文章,作者微信公眾號:喔家ArchiSelf,id:wrieless-com】
網(wǎng)站題目:談?wù)剰倪B接池到內(nèi)存池
文章轉(zhuǎn)載:http://m.5511xx.com/article/ccspohh.html


咨詢
建站咨詢
