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

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


咨詢(xún)
建站咨詢(xún)
