張帥峰,周 昕,劉繼興,曾令輝,段珍靈,沈順權(quán)
(哈爾濱理工大學(xué) 計(jì)算機(jī)科學(xué)與技術(shù)學(xué)院,哈爾濱 150080)
隨著數(shù)字生活的深入發(fā)展,計(jì)算機(jī)系統(tǒng)越發(fā)面臨高并發(fā)的考驗(yàn)。由圖1 可知,從各年每秒訂單交易量的峰值數(shù)和增長趨勢中,我們可以觀察到這一點(diǎn)。所以,高并發(fā)(High Concurrency)是互聯(lián)網(wǎng)分布式系統(tǒng)架構(gòu)中必須考慮的因素之一。它通常是指通過設(shè)計(jì)保證系統(tǒng)能夠同時(shí)并行處理許多請求。高并發(fā)的基本表現(xiàn)為單位時(shí)間內(nèi)系統(tǒng)能夠同時(shí)處理的請求數(shù),核心是對CPU 資源的有效壓榨。當(dāng)大量的用戶向服務(wù)器連接,發(fā)送請求時(shí),服務(wù)器的高并發(fā)直觀影響用戶的體驗(yàn)。
圖1 各年每秒訂單量峰值圖
從衡量標(biāo)準(zhǔn)來看,不同的場景對應(yīng)不同的需求,我們對于高并發(fā)可以有不同的衡量方式。
從網(wǎng)絡(luò)的傳輸層來看,TCP 傳輸層通信協(xié)議會有3 處握手來進(jìn)行連接,與UDP 相比具有更高的可靠性和穩(wěn)定性。TCP 服務(wù)器是最基本也是最常用的服務(wù)器,是其他并發(fā)服務(wù)器于傳輸層上的基石。
從數(shù)據(jù)的收發(fā)來看,Socket 是計(jì)算機(jī)之間進(jìn)行通信的一種約定或一種方式。通過socket 這種約定,1 臺計(jì)算機(jī)可以接收其他計(jì)算機(jī)的數(shù)據(jù),也可以向其他計(jì)算機(jī)發(fā)送數(shù)據(jù)。使用socket 對數(shù)據(jù)流進(jìn)行收發(fā)操作就是網(wǎng)絡(luò)I/O 操作。在網(wǎng)絡(luò)環(huán)境下,I/O 操作會經(jīng)歷2 個(gè)階段:等待數(shù)據(jù)準(zhǔn)備就緒,將數(shù)據(jù)從內(nèi)核拷貝到進(jìn)程或者線程中。因?yàn)樵谝陨? 個(gè)階段上各有不同的情況,所以出現(xiàn)了多種網(wǎng)絡(luò)I/O 模型。多種網(wǎng)絡(luò)I/O 模型為TCP并發(fā)服務(wù)器提供了不同的實(shí)現(xiàn)方案。常見的2 種方案是阻塞I/O 模型下的多線程和I/O 多路復(fù)用下的epoll。
每秒查詢數(shù)QPS(Query Per Second):是衡量吞吐量(Throughput)的一個(gè)常用指標(biāo)。在服務(wù)器并發(fā)上,通常是指服務(wù)器每秒能夠處理的查詢次數(shù),是對1 個(gè)特定的查詢服務(wù)器在規(guī)定時(shí)間內(nèi)所處理流量多少的衡量標(biāo)準(zhǔn)。在因特網(wǎng)上,作為域名系統(tǒng)服務(wù)器的機(jī)器的性能經(jīng)常用每秒查詢率來衡量。每秒的響應(yīng)請求數(shù),也就是最大吞吐能力。
每秒事務(wù)數(shù)TPS(Transactions Per Second):是軟件測試結(jié)果的測量單位。一個(gè)事務(wù)是指1 個(gè)客戶端向服務(wù)器發(fā)送請求,然后服務(wù)器做出響應(yīng)的過程。具體來說,每個(gè)事務(wù)包括向服務(wù)器發(fā)請求,服務(wù)器自己的內(nèi)部處理與服務(wù)器返回結(jié)果給客戶端3 個(gè)過程。
TPS 與QPS 是不同的概念,TPS 是每秒事務(wù)數(shù),每一個(gè)事務(wù)的過程中,可能產(chǎn)生多次對服務(wù)器的請求,每次請求都算QPS 的查詢數(shù)。所以,一個(gè)TPS 可能包含多個(gè)QPS。
客戶端在發(fā)送請求時(shí)開始計(jì)時(shí),收到服務(wù)器響應(yīng)后結(jié)束計(jì)時(shí),以此來計(jì)算使用的時(shí)間和完成的事務(wù)個(gè)數(shù)。
響應(yīng)時(shí)間(Response-time):執(zhí)行一個(gè)請求從開始到最后收到響應(yīng)數(shù)據(jù)所花費(fèi)的總體時(shí)間,即從客戶端發(fā)起請求到收到服務(wù)器響應(yīng)結(jié)果的時(shí)間。該請求可以是任何東西,從內(nèi)存獲取,磁盤I/O,復(fù)雜的數(shù)據(jù)庫查詢或加載完整的網(wǎng)頁。如果我們不計(jì)極小的傳輸時(shí)間,響應(yīng)時(shí)間就是處理時(shí)間和等待時(shí)間的總和。處理時(shí)間是完成請求要求的工作所需的時(shí)間,等待時(shí)間是請求在被處理之前必須在隊(duì)列中等待的時(shí)間。響應(yīng)時(shí)間是一個(gè)系統(tǒng)最重要的指標(biāo)之一,它的數(shù)值大小直接反映了系統(tǒng)的快慢,基本和用戶體驗(yàn)息息相關(guān)。
并發(fā)數(shù)(Concurrency)是指系統(tǒng)同時(shí)能處理的請求數(shù)量,這個(gè)也反映了系統(tǒng)的負(fù)載能力。并發(fā)意味著可以同時(shí)進(jìn)行多個(gè)處理。并發(fā)在現(xiàn)代編程中無處不在,網(wǎng)絡(luò)中有多臺計(jì)算機(jī)同時(shí)存在,1 臺計(jì)算機(jī)上同時(shí)運(yùn)行著多個(gè)應(yīng)用程序。
吞吐量(Throughput)是每秒承受的用戶訪問量,吞吐量(系統(tǒng)能承受多少壓力)和當(dāng)前請求對CPU 消耗、內(nèi)存、I/O 使用等緊密相關(guān)。單個(gè)請求消耗越高,系統(tǒng)吞吐量越低,反之越高。
一個(gè)系統(tǒng)的吞吐量和其TPS、QPS、并發(fā)數(shù)息息相關(guān),每個(gè)系統(tǒng)針對這些值都有一個(gè)相對極限值,只要其中某一個(gè)達(dá)到最大,系統(tǒng)的吞吐量也就到達(dá)極限了。如此時(shí)壓力繼續(xù)增大,系統(tǒng)的吞吐量反而會下降,原因是系統(tǒng)超負(fù)荷工作,各種資源切換等的消耗導(dǎo)致系統(tǒng)性能下降。
之后,我們進(jìn)行壓力測試時(shí),使用QPS 和并發(fā)數(shù)
作為TCP 服務(wù)器并發(fā)的衡量指標(biāo)。
阻塞I/O,在I/O 執(zhí)行的兩個(gè)階段(等待數(shù)據(jù)和拷貝數(shù)據(jù)兩個(gè)階段)都會被阻塞。大部分socket 接口如listen()、send()和recv()等都是阻塞型的。當(dāng)這些接口被阻塞時(shí),調(diào)用這些接口的進(jìn)程或者線程將會被阻塞,在此期間,進(jìn)程或線程將無法執(zhí)行任何運(yùn)算或響應(yīng)任何的網(wǎng)絡(luò)請求。所以,使用這些接口可以很方便地構(gòu)建一問一答的服務(wù)器/客戶機(jī)的模型,但在單進(jìn)程/線程情況下,服務(wù)器不能同時(shí)處理多個(gè)客戶機(jī)的請求。
一個(gè)簡單的改進(jìn)方案是在服務(wù)器端使用多線程(或多進(jìn)程)。多線程(或多進(jìn)程)的目的是讓每個(gè)連接都擁有獨(dú)立的線程(或進(jìn)程),這樣任何一個(gè)連接的阻塞都不會影響其他的連接。通常,使用pthread_create()創(chuàng)建新線程,fork()創(chuàng)建新進(jìn)程。多線程服務(wù)器/客戶端模型如圖2 所示。
圖2 多線程服務(wù)器/客戶端圖
多線程服務(wù)器解決了為多個(gè)客戶機(jī)提供問答服務(wù)的要求,但當(dāng)客戶機(jī)數(shù)量有一定規(guī)模,同時(shí)發(fā)出成百上千路的連接請求,服務(wù)器隨之產(chǎn)生的多線程會嚴(yán)重占據(jù)系統(tǒng)資源,降低系統(tǒng)對外界響應(yīng)效率。一種解決的方案是用線程池維持一定合理數(shù)量的線程,并讓空閑的線程重新承擔(dān)新的執(zhí)行任務(wù)。此方案確實(shí)在一定程度上緩解了頻繁調(diào)用I/O 接口帶來的資源占用。但也只是增大了上限,客戶機(jī)達(dá)到一定數(shù)量級時(shí),此方案并不會改善響應(yīng)效率。
這種模型的特征在于單個(gè)線程就可以同時(shí)處理多個(gè)網(wǎng)絡(luò)連接的I/O,它的基本原理就是select/epoll 會不斷地輪詢所負(fù)責(zé)的所有socket,當(dāng)某個(gè)socket 有數(shù)據(jù)到達(dá)了,就通知用戶進(jìn)程,讓用戶進(jìn)程去處理。使用一個(gè)進(jìn)程就實(shí)現(xiàn)了阻塞I/O 需要多個(gè)線程來同時(shí)處理多個(gè)socket 的I/O 請求的功能。
阻塞I/O 模型使用多個(gè)線程處理多個(gè)I/O 請求的優(yōu)勢在于單個(gè)連接能處理得快,但連接數(shù)增大時(shí),大量的線程會嚴(yán)重占據(jù)系統(tǒng)資源,降低系統(tǒng)對外界響應(yīng)效率。多路復(fù)用I/O 使用一個(gè)不斷輪詢所有socket,能處理更多的連接,單個(gè)連接處理未必能優(yōu)于阻塞I/O 多線程。
3.1.1 兩臺虛擬機(jī)
其中1 臺4 GB 內(nèi)存,4 個(gè)處理器的虛擬機(jī)作為服務(wù)器;另1 臺2 GB 內(nèi)存,2 個(gè)處理器的虛擬機(jī)作為客戶機(jī)。如圖3 所示。
圖3 實(shí)驗(yàn)虛擬機(jī)配置圖
3.1.2 虛擬機(jī)環(huán)境配置
(1)修改/etc/security/limits.conf
在limits.conf 文件中添加
* soft nofile 1048576
* hard nofile 1048576
同時(shí),在工作保障方面,區(qū)工商聯(lián)完善商會調(diào)解工作體系,提高維權(quán)服務(wù)水平,成功調(diào)解債權(quán)債務(wù)糾紛150余件;積極引導(dǎo)非公經(jīng)濟(jì)人士積極履行社會責(zé)任,自覺投身光彩事業(yè),合力打贏脫貧攻堅(jiān)戰(zhàn),共組織公益活動50余次,捐款、捐物合計(jì)1500余萬元;積極參政議政,向各級人大、政協(xié)組織推薦代表委員13人,提交提案150余件;開展微型企業(yè)培育工程,累計(jì)為1400余人發(fā)放貸款9000余萬元,帶動就業(yè)4000余人。
(2)載入ip_conntrack 模塊
sudo modprobe ip_conntrack
(3)修改/etc/sysctl.conf
在sysctl.conf 文件中添加
net.ipv4.tcp_mem = 252144 524288 786432
net.ipv4.tcp_wmem = 1024 1024 2048
net.ipv4.tcp_rmem = 1024 1024 2048
fs.file-max = 1048576
net.nf_conntrack_max = 1048576
(4)更新配置
sudo sysctl -p
3.1.3 運(yùn)行程序
客戶端程序:mul_client.c 用來模擬大量客戶端與服務(wù)器進(jìn)行連接,持續(xù)發(fā)送Hello Server 和已經(jīng)與服務(wù)器連接的客戶端數(shù)量。
服務(wù)端程序:tcp_server.c 用來實(shí)現(xiàn)一請求一線程的多線程和I/O 多路復(fù)用的epoll 兩種TCP 服務(wù)器方式。
服務(wù)器:./tcp_server.out 8888
客戶端:./mul_client.out 192.168.127.138 8888
實(shí)驗(yàn)結(jié)果見表1,通過實(shí)驗(yàn)結(jié)果對比發(fā)現(xiàn),并發(fā)數(shù)上epoll 方案遠(yuǎn)大于多線程方案,連接速率上兩方案基本相等。綜合來看,顯然epoll 方案更優(yōu)。
表1 壓力測試結(jié)果圖
本文詳細(xì)講解了服務(wù)器并發(fā)的衡量指標(biāo),并根據(jù)I/O 網(wǎng)絡(luò)模型總結(jié)了2 種TCP 服務(wù)器的并發(fā)方案。在同一環(huán)境下,對2 種并發(fā)方案進(jìn)行了壓力測試,得出e poll 方案更優(yōu),為后續(xù)項(xiàng)目網(wǎng)絡(luò)通作奠定了基礎(chǔ)。