張帥峰,周 昕,劉繼興,曾令輝,段珍靈,沈順權
(哈爾濱理工大學 計算機科學與技術學院,哈爾濱 150080)
隨著數字生活的深入發展,計算機系統越發面臨高并發的考驗。由圖1 可知,從各年每秒訂單交易量的峰值數和增長趨勢中,我們可以觀察到這一點。所以,高并發(High Concurrency)是互聯網分布式系統架構中必須考慮的因素之一。它通常是指通過設計保證系統能夠同時并行處理許多請求。高并發的基本表現為單位時間內系統能夠同時處理的請求數,核心是對CPU 資源的有效壓榨。當大量的用戶向服務器連接,發送請求時,服務器的高并發直觀影響用戶的體驗。

圖1 各年每秒訂單量峰值圖
從衡量標準來看,不同的場景對應不同的需求,我們對于高并發可以有不同的衡量方式。
從網絡的傳輸層來看,TCP 傳輸層通信協議會有3 處握手來進行連接,與UDP 相比具有更高的可靠性和穩定性。TCP 服務器是最基本也是最常用的服務器,是其他并發服務器于傳輸層上的基石。
從數據的收發來看,Socket 是計算機之間進行通信的一種約定或一種方式。通過socket 這種約定,1 臺計算機可以接收其他計算機的數據,也可以向其他計算機發送數據。使用socket 對數據流進行收發操作就是網絡I/O 操作。在網絡環境下,I/O 操作會經歷2 個階段:等待數據準備就緒,將數據從內核拷貝到進程或者線程中。因為在以上2 個階段上各有不同的情況,所以出現了多種網絡I/O 模型。多種網絡I/O 模型為TCP并發服務器提供了不同的實現方案。常見的2 種方案是阻塞I/O 模型下的多線程和I/O 多路復用下的epoll。
每秒查詢數QPS(Query Per Second):是衡量吞吐量(Throughput)的一個常用指標。在服務器并發上,通常是指服務器每秒能夠處理的查詢次數,是對1 個特定的查詢服務器在規定時間內所處理流量多少的衡量標準。在因特網上,作為域名系統服務器的機器的性能經常用每秒查詢率來衡量。每秒的響應請求數,也就是最大吞吐能力。
每秒事務數TPS(Transactions Per Second):是軟件測試結果的測量單位。一個事務是指1 個客戶端向服務器發送請求,然后服務器做出響應的過程。具體來說,每個事務包括向服務器發請求,服務器自己的內部處理與服務器返回結果給客戶端3 個過程。
TPS 與QPS 是不同的概念,TPS 是每秒事務數,每一個事務的過程中,可能產生多次對服務器的請求,每次請求都算QPS 的查詢數。所以,一個TPS 可能包含多個QPS。
客戶端在發送請求時開始計時,收到服務器響應后結束計時,以此來計算使用的時間和完成的事務個數。
響應時間(Response-time):執行一個請求從開始到最后收到響應數據所花費的總體時間,即從客戶端發起請求到收到服務器響應結果的時間。該請求可以是任何東西,從內存獲取,磁盤I/O,復雜的數據庫查詢或加載完整的網頁。如果我們不計極小的傳輸時間,響應時間就是處理時間和等待時間的總和。處理時間是完成請求要求的工作所需的時間,等待時間是請求在被處理之前必須在隊列中等待的時間。響應時間是一個系統最重要的指標之一,它的數值大小直接反映了系統的快慢,基本和用戶體驗息息相關。
并發數(Concurrency)是指系統同時能處理的請求數量,這個也反映了系統的負載能力。并發意味著可以同時進行多個處理。并發在現代編程中無處不在,網絡中有多臺計算機同時存在,1 臺計算機上同時運行著多個應用程序。
吞吐量(Throughput)是每秒承受的用戶訪問量,吞吐量(系統能承受多少壓力)和當前請求對CPU 消耗、內存、I/O 使用等緊密相關。單個請求消耗越高,系統吞吐量越低,反之越高。
一個系統的吞吐量和其TPS、QPS、并發數息息相關,每個系統針對這些值都有一個相對極限值,只要其中某一個達到最大,系統的吞吐量也就到達極限了。如此時壓力繼續增大,系統的吞吐量反而會下降,原因是系統超負荷工作,各種資源切換等的消耗導致系統性能下降。
之后,我們進行壓力測試時,使用QPS 和并發數
作為TCP 服務器并發的衡量指標。
阻塞I/O,在I/O 執行的兩個階段(等待數據和拷貝數據兩個階段)都會被阻塞。大部分socket 接口如listen()、send()和recv()等都是阻塞型的。當這些接口被阻塞時,調用這些接口的進程或者線程將會被阻塞,在此期間,進程或線程將無法執行任何運算或響應任何的網絡請求。所以,使用這些接口可以很方便地構建一問一答的服務器/客戶機的模型,但在單進程/線程情況下,服務器不能同時處理多個客戶機的請求。
一個簡單的改進方案是在服務器端使用多線程(或多進程)。多線程(或多進程)的目的是讓每個連接都擁有獨立的線程(或進程),這樣任何一個連接的阻塞都不會影響其他的連接。通常,使用pthread_create()創建新線程,fork()創建新進程。多線程服務器/客戶端模型如圖2 所示。

圖2 多線程服務器/客戶端圖
多線程服務器解決了為多個客戶機提供問答服務的要求,但當客戶機數量有一定規模,同時發出成百上千路的連接請求,服務器隨之產生的多線程會嚴重占據系統資源,降低系統對外界響應效率。一種解決的方案是用線程池維持一定合理數量的線程,并讓空閑的線程重新承擔新的執行任務。此方案確實在一定程度上緩解了頻繁調用I/O 接口帶來的資源占用。但也只是增大了上限,客戶機達到一定數量級時,此方案并不會改善響應效率。
這種模型的特征在于單個線程就可以同時處理多個網絡連接的I/O,它的基本原理就是select/epoll 會不斷地輪詢所負責的所有socket,當某個socket 有數據到達了,就通知用戶進程,讓用戶進程去處理。使用一個進程就實現了阻塞I/O 需要多個線程來同時處理多個socket 的I/O 請求的功能。
阻塞I/O 模型使用多個線程處理多個I/O 請求的優勢在于單個連接能處理得快,但連接數增大時,大量的線程會嚴重占據系統資源,降低系統對外界響應效率。多路復用I/O 使用一個不斷輪詢所有socket,能處理更多的連接,單個連接處理未必能優于阻塞I/O 多線程。
3.1.1 兩臺虛擬機
其中1 臺4 GB 內存,4 個處理器的虛擬機作為服務器;另1 臺2 GB 內存,2 個處理器的虛擬機作為客戶機。如圖3 所示。

圖3 實驗虛擬機配置圖
3.1.2 虛擬機環境配置
(1)修改/etc/security/limits.conf
在limits.conf 文件中添加
* soft nofile 1048576
* hard nofile 1048576
同時,在工作保障方面,區工商聯完善商會調解工作體系,提高維權服務水平,成功調解債權債務糾紛150余件;積極引導非公經濟人士積極履行社會責任,自覺投身光彩事業,合力打贏脫貧攻堅戰,共組織公益活動50余次,捐款、捐物合計1500余萬元;積極參政議政,向各級人大、政協組織推薦代表委員13人,提交提案150余件;開展微型企業培育工程,累計為1400余人發放貸款9000余萬元,帶動就業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 運行程序
客戶端程序:mul_client.c 用來模擬大量客戶端與服務器進行連接,持續發送Hello Server 和已經與服務器連接的客戶端數量。
服務端程序:tcp_server.c 用來實現一請求一線程的多線程和I/O 多路復用的epoll 兩種TCP 服務器方式。
服務器:./tcp_server.out 8888
客戶端:./mul_client.out 192.168.127.138 8888
實驗結果見表1,通過實驗結果對比發現,并發數上epoll 方案遠大于多線程方案,連接速率上兩方案基本相等。綜合來看,顯然epoll 方案更優。

表1 壓力測試結果圖
本文詳細講解了服務器并發的衡量指標,并根據I/O 網絡模型總結了2 種TCP 服務器的并發方案。在同一環境下,對2 種并發方案進行了壓力測試,得出e poll 方案更優,為后續項目網絡通作奠定了基礎。