作者簡介:謝友鵬,目前在螞蟻金服-支付寶-網(wǎng)絡(luò)技術(shù)部任職技術(shù)專家,先后在華為和支付寶從事網(wǎng)絡(luò)和云相關(guān)開發(fā)。涉及領(lǐng)域vpn、sdwan、全球網(wǎng)絡(luò)加速、區(qū)塊鏈網(wǎng)絡(luò)加速、cdn靜態(tài)緩存等。長期研究nginx、apache traffic server、frp等代理項(xiàng)目和基于k8s的云原生項(xiàng)目。不定期更新個(gè)人微信公眾號(hào)--網(wǎng)絡(luò)技術(shù)修煉。關(guān)注領(lǐng)域:網(wǎng)絡(luò)、云、linux高性能服務(wù)端。
1999年Dan Kegel在發(fā)表的論文中提出了The C10K problem,這篇論文對(duì)傳統(tǒng)服務(wù)器架構(gòu)處理大規(guī)模并發(fā)連接時(shí)的挑戰(zhàn)進(jìn)行了詳細(xì)描述,并提出了一些解決方案和優(yōu)化技術(shù)。這里的C指的是Concurrent(并發(fā))的縮寫,C10K問題是指怎么在單臺(tái)服務(wù)器上并發(fā)一萬個(gè)請(qǐng)求。如果你分析過性能問題一定會(huì)注意到,性能極限通常受到一個(gè)或多個(gè)資源的限制,比如內(nèi)存、文件句柄個(gè)數(shù)、網(wǎng)絡(luò)帶寬、CPU等。這里討論的前提是機(jī)器的物理資源和系統(tǒng)配置能夠滿足一萬個(gè)請(qǐng)求。在這個(gè)前提下,網(wǎng)絡(luò)并發(fā)主要關(guān)注兩個(gè)方面:一是應(yīng)用程序和操作系統(tǒng)內(nèi)核之間如何進(jìn)行IO事件通知,二是應(yīng)用程序進(jìn)程或線程的分配方式。
I/O模型
阻塞vs非阻塞、同步vs異步
你可能經(jīng)??吹侥衬稠?xiàng)目用的同步非阻塞模型、某某項(xiàng)目用的異步非阻塞模型,所以在正式討論I/O模型前,還是先對(duì)齊一下關(guān)于阻塞、非阻塞以及同步、異步的概念吧。
阻塞、非阻塞指的是系統(tǒng)調(diào)用時(shí)“等待數(shù)據(jù)準(zhǔn)備好”這個(gè)動(dòng)作。比如read時(shí)候,如果內(nèi)核判定數(shù)據(jù)還沒準(zhǔn)備好:
內(nèi)核讓應(yīng)用進(jìn)程一直等待,直到數(shù)據(jù)準(zhǔn)備好才通知應(yīng)用進(jìn)程就是阻塞;
內(nèi)核立即通知應(yīng)用程序說數(shù)據(jù)沒準(zhǔn)備好,你先干別的吧,就是非阻塞。
同步、異步指的是“內(nèi)核空間與用戶空間復(fù)制數(shù)據(jù)”這個(gè)動(dòng)作。比如read時(shí)候,如果內(nèi)核判定數(shù)據(jù)還沒準(zhǔn)備,過一段時(shí)間數(shù)據(jù)來了:
內(nèi)核通知應(yīng)用進(jìn)程你來讀取數(shù)據(jù)吧,應(yīng)用程序再次系統(tǒng)調(diào)用將數(shù)據(jù)讀走就是同步;
內(nèi)核將數(shù)據(jù)拷貝到用戶空間后再通知應(yīng)用進(jìn)程,數(shù)據(jù)已經(jīng)拷貝好了直接用吧,就是異步。
I/O模型詳解
Stevens在《UNIX 網(wǎng)絡(luò)編程 卷1》一書的6.2章節(jié)介紹了五種 I/O 模型。以下模型以UDP接收?qǐng)?bào)文為例來說明這五種I/O 的工作方式。
阻塞式I/O
該模型使用最簡,用戶進(jìn)程調(diào)用讀取數(shù)據(jù)系統(tǒng)后就一直等待,直到內(nèi)核數(shù)據(jù)準(zhǔn)備好,并將數(shù)據(jù)從內(nèi)核空間拷貝到用戶空間后,調(diào)用結(jié)束。這種模型效率顯然的低下,因?yàn)檫@種模型會(huì)導(dǎo)致兩種可能結(jié)果:
為每個(gè)請(qǐng)求分配一個(gè)進(jìn)程或線程,那么高并發(fā)意味著內(nèi)核要調(diào)度的進(jìn)程或線程數(shù)量很龐大,調(diào)度、上下文切換等開銷會(huì)使系統(tǒng)性能降低。
固定數(shù)量的進(jìn)程或線程處理請(qǐng)求,那么這些進(jìn)程或線程全被被占用后,新請(qǐng)求就只能等了。
該模型低效的根本原因在于阻塞,內(nèi)核數(shù)據(jù)沒有準(zhǔn)備好的時(shí)候,用戶態(tài)進(jìn)程明明可以干其他活的,現(xiàn)在只能白白等待。
非阻塞式 I/O
這種模型通過非阻塞的方式與內(nèi)核打交道,如果內(nèi)核中數(shù)據(jù)還未準(zhǔn)備好,就立刻返回給用戶進(jìn)程,用戶進(jìn)程就可以先干別的事情,過一段再進(jìn)行讀數(shù)據(jù)的系統(tǒng)調(diào)用,直到內(nèi)核數(shù)據(jù)準(zhǔn)備好,并將數(shù)據(jù)拷貝到用戶空間。相比于阻塞的模型,這種非阻塞+主動(dòng)輪詢的模型避免了用戶進(jìn)程白白等待內(nèi)核準(zhǔn)備數(shù)據(jù)的時(shí)間,所以效率有所提升,但是因?yàn)槊看屋喸兌际窍到y(tǒng)調(diào)用,所以上下文切換變多了,因此性能也不高。
I/O 復(fù)用
既然不停主動(dòng)查詢內(nèi)核數(shù)據(jù)是否準(zhǔn)備好這件事會(huì)引起系統(tǒng)性能下降,那能不能通過注冊(cè)+通知的方式呢?這就是大名鼎鼎的I/O復(fù)用模型。該模型允許用戶態(tài)通過一個(gè)進(jìn)程將所有相關(guān)的讀寫事件(使用select、poll或epoll)注冊(cè)到內(nèi)核,然后內(nèi)核會(huì)主動(dòng)通知用戶態(tài)進(jìn)程,一旦任意一個(gè)或多個(gè)請(qǐng)求的讀寫數(shù)據(jù)準(zhǔn)備好。這種方式在單個(gè)進(jìn)程或線程中同時(shí)處理多個(gè)I/O通道的就緒狀態(tài)被稱為I/O多路復(fù)用。使用I/O多路復(fù)用既不會(huì)阻塞處理請(qǐng)求的進(jìn)程,也不會(huì)因?yàn)檩喸儍?nèi)核數(shù)據(jù)是否準(zhǔn)備好而導(dǎo)致過多的系統(tǒng)調(diào)用,因此具有高效的特點(diǎn)。然而,需要注意的是,一旦內(nèi)核通知應(yīng)用進(jìn)程數(shù)據(jù)準(zhǔn)備就緒,仍然需要通過系統(tǒng)調(diào)用觸發(fā)數(shù)據(jù)的讀取過程。
Linux內(nèi)核對(duì)這種模型的支持非常完善,因此許多高性能服務(wù)器在Linux環(huán)境中廣泛采用這種模式。
信號(hào)驅(qū)動(dòng)式 I/O
I/O復(fù)用模型中是用一個(gè)進(jìn)程(select、poll或epoll)阻塞或輪詢所有請(qǐng)求的數(shù)據(jù)是否準(zhǔn)備好,從而讓所有請(qǐng)求進(jìn)程的處理都不會(huì)阻塞。信號(hào)驅(qū)動(dòng)式則沒有這個(gè)復(fù)用的I/O進(jìn)程,每個(gè)請(qǐng)求進(jìn)程自己去內(nèi)核注冊(cè),然后等數(shù)據(jù)準(zhǔn)備好內(nèi)核通知應(yīng)用進(jìn)程去處理。這種模型應(yīng)用套接字處理的實(shí)踐場景為基于UDP的NTP服務(wù),幾乎沒有在TCP上的應(yīng)用,因?yàn)閷?duì)于TCP來說信號(hào)產(chǎn)生過于頻繁,而且并沒有告訴應(yīng)用程序發(fā)生了什么事件,比如下面條件均會(huì)導(dǎo)致TCP套接字產(chǎn)生SIGIO信號(hào):
監(jiān)聽套接字某個(gè)連接請(qǐng)求已經(jīng)完成;
某個(gè)斷鏈請(qǐng)求已經(jīng)發(fā)起;
某個(gè)斷鏈請(qǐng)求已經(jīng)完成;
某個(gè)半連接已經(jīng)關(guān)閉;
數(shù)據(jù)到達(dá)套接字;
數(shù)據(jù)已經(jīng)從套接字發(fā)出;
發(fā)生某個(gè)異步錯(cuò)誤。
異步 I/O
前面幾種方式,無論是阻塞還是非阻塞,從內(nèi)核空間到用戶空間復(fù)制數(shù)據(jù)的動(dòng)作都是在內(nèi)核通知用戶進(jìn)程后,用戶進(jìn)程再通過系統(tǒng)調(diào)用觸發(fā)完成的,因此都屬于同步操作。而異步I/O模型則不同,它允許用戶態(tài)進(jìn)程通過系統(tǒng)調(diào)用讀取數(shù)據(jù)后,即使內(nèi)核數(shù)據(jù)未準(zhǔn)備好,也會(huì)立即返回給用戶進(jìn)程,告知數(shù)據(jù)未準(zhǔn)備好,讓用戶進(jìn)程可以執(zhí)行其他操作。當(dāng)數(shù)據(jù)準(zhǔn)備好后,內(nèi)核會(huì)將數(shù)據(jù)從內(nèi)核空間拷貝到用戶態(tài),并直接回調(diào)用戶進(jìn)程,將數(shù)據(jù)送到用戶進(jìn)程手中。這種模型不僅具備非阻塞特性,還能進(jìn)一步減少系統(tǒng)調(diào)用的次數(shù),因此在理論上相對(duì)于其他模型更加高效。需要注意的是,這種模型需要操作系統(tǒng)內(nèi)核的支持。
在《UNIX網(wǎng)絡(luò)編程卷1》一書中,截至?xí)鍟r(shí),支持POSIX異步I/O的系統(tǒng)相對(duì)較少。由于早期Linux內(nèi)核對(duì)網(wǎng)絡(luò)異步I/O的支持不夠成熟,在Linux環(huán)境下,大多數(shù)高性能網(wǎng)絡(luò)服務(wù)器選擇采用I/O復(fù)用的方式,如epoll。然而,從Linux內(nèi)核5.0版本開始,引入了io_uring異步操作,隨著該技術(shù)的成熟,越來越多的高性能網(wǎng)絡(luò)服務(wù)器(例如nginx)開始支持使用這種異步I/O方式。
如何簡單理解5種I/O?
下面通過一個(gè)例子對(duì)比一下5種模型,顧客是應(yīng)用進(jìn)程,餐飲人員為內(nèi)核,餐桌為應(yīng)用進(jìn)程的數(shù)據(jù)buffer:
阻塞式I/O:交完錢也要在窗口排隊(duì),等師傅做好,將飯端給你,你再端到自己餐桌。
非阻塞式I/O:交完錢你就可以離開窗口玩一會(huì)了,窗口有個(gè)屏幕,你過一會(huì)跑過來看一下自己的飯好了沒,直到飯做好,自己端到自己的餐桌。
I/O復(fù)用:好幾個(gè)同學(xué)都把飯卡交給你,你一個(gè)人跑到窗口排隊(duì)刷卡,誰的飯好了,你就打電話給誰,讓他自己將飯端到餐桌。
信號(hào)驅(qū)動(dòng)式I/O:你去窗口手機(jī)刷卡后就可離開了,飯做好會(huì)通過手機(jī)通知你,然后自己過去將飯端到餐桌。
異步I/O:去窗口點(diǎn)餐后,告訴服務(wù)員你在哪個(gè)餐桌就可以離開了,飯做好,服務(wù)員會(huì)將飯幫你端到餐桌。
進(jìn)程/線程分配
進(jìn)程和線程的創(chuàng)建、調(diào)度都需要系統(tǒng)開銷。在高并發(fā)系統(tǒng)中,為每個(gè)請(qǐng)求分配一個(gè)進(jìn)程或線程會(huì)對(duì)性能產(chǎn)生不利影響。為了克服這個(gè)問題,高性能的網(wǎng)絡(luò)模型通常采用進(jìn)程池或線程池來管理進(jìn)程或線程。進(jìn)程/線程池的設(shè)計(jì)目的是降低創(chuàng)建和銷毀進(jìn)程/線程的頻率,并限制系統(tǒng)中總進(jìn)程/線程的數(shù)量,以減少內(nèi)核調(diào)度的開銷。
常用高性能模式
reactor 模式
《The Design and Implementation of the Reactor》一文詳細(xì)介紹了reactor模式的工作方式,簡單來說,reactor模式=I/O復(fù)用 + 進(jìn)程池/線程池。
proactor模式
《Proactor: An Object Behavioral Pattern for Demultiplexing and Dispatching Handlers for Asynchronous Events》一文詳細(xì)介紹了proactor模式的工作方式,簡單來說,proactor模式=異步I/O+ 進(jìn)程池/線程池。
驚群效應(yīng)
對(duì)于TCP請(qǐng)求來說,最理想的情況是每個(gè)事件每次從池中喚醒一個(gè)進(jìn)程或線程去執(zhí)行,這樣既不需要等待又不會(huì)引起競爭。用一個(gè)進(jìn)程或線程專門負(fù)責(zé)處理accept事件,然后將接下來的事件繼續(xù)分發(fā)給其他worker處理是可行的。有一些模型(比如nginx)存在多個(gè)進(jìn)程或線程監(jiān)聽同一個(gè)端口的情況,如果不加處理會(huì)出現(xiàn)一個(gè)accept事件喚醒所有worker進(jìn)程的情況,即驚群效應(yīng)。為了應(yīng)對(duì)驚群效應(yīng),早期nginx引入了accept_mutex的機(jī)制,競爭到鎖的worker才會(huì)執(zhí)行accept操作,從而避免所有worker都被喚醒。Linux3.9 版本后提供了reuseport更好的解決了多個(gè)進(jìn)程或線程監(jiān)聽同一個(gè)端口引起的驚群問題,簡單說就是內(nèi)核幫你輪詢,而不用在應(yīng)用層面競爭了。
更快、更強(qiáng)大的網(wǎng)絡(luò)模型
欲望是永無止境的,有人提出The C10K problem問題,就有人提出The C10M problem,前文中的討論基本都是圍繞用戶態(tài)進(jìn)程和內(nèi)核的交互優(yōu)化,既然和內(nèi)核交互容易導(dǎo)致性能瓶頸,那為何不旁路掉內(nèi)核協(xié)議棧呢?所以有了更快的網(wǎng)絡(luò)方案,比如DPDK、XDP、甚至硬件加速等。
審核編輯:湯梓紅
-
Linux
+關(guān)注
關(guān)注
87文章
11509瀏覽量
213674 -
服務(wù)器
+關(guān)注
關(guān)注
13文章
9786瀏覽量
87900 -
UDP
+關(guān)注
關(guān)注
0文章
330瀏覽量
34628 -
網(wǎng)絡(luò)模型
+關(guān)注
關(guān)注
0文章
44瀏覽量
8750
原文標(biāo)題:高性能服務(wù)器網(wǎng)絡(luò)模型詳解
文章出處:【微信號(hào):SDNLAB,微信公眾號(hào):SDNLAB】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄

服務(wù)器技術(shù)基礎(chǔ)
高性能高并發(fā)服務(wù)器架構(gòu)分享
服務(wù)器的特性與劃分
基于OPNET實(shí)現(xiàn)跨層網(wǎng)絡(luò)服務(wù)器模型的構(gòu)型

服務(wù)器的功能_服務(wù)器是干嘛的
詳解Nginx高性能的HTTP和反向代理服務(wù)器
用高性能服務(wù)器優(yōu)化大型HFSS模型
人工智能服務(wù)器高性能計(jì)算需求
GPU高性能服務(wù)器配置
高性能云服務(wù)器有什么用處?
GPU服務(wù)器AI網(wǎng)絡(luò)架構(gòu)設(shè)計(jì)

評(píng)論