對(duì)“大量請(qǐng)求造成網(wǎng)絡(luò)負(fù)載”的理解
這里指的服務(wù)器是指提供HTTP服務(wù)的服務(wù)器,人們通常衡量一臺(tái)web服務(wù)器能力的大小為其在單位時(shí)間內(nèi)能處理的請(qǐng)求數(shù)的多少。
3.1 吞吐率
Web服務(wù)器的吞吐率是指其單位時(shí)間內(nèi)所能處理的請(qǐng)求數(shù)。更關(guān)心的是服務(wù)器并發(fā)處理能力的上限即最大吞吐率。
Web服務(wù)器在實(shí)際工作中,其處理的Http請(qǐng)求包括對(duì)很多不同資源的請(qǐng)求即請(qǐng)求的url不一樣。正因?yàn)檫@種請(qǐng)求性質(zhì)的不同,Web服務(wù)器并發(fā)能力的強(qiáng)弱關(guān)鍵在于如何針對(duì)不同的請(qǐng)求性質(zhì)設(shè)計(jì)不同的并發(fā)策略。有時(shí)候一臺(tái)Web服務(wù)器要同時(shí)處理許多不同性質(zhì)的請(qǐng)求,在一程度上使得Web服務(wù)器性能無(wú)法發(fā)揮。
并發(fā)用戶(hù)數(shù)為某一時(shí)刻同時(shí)向服務(wù)器發(fā)送請(qǐng)求的用戶(hù)數(shù)。注意,100個(gè)用戶(hù)同時(shí)向服務(wù)器各發(fā)10個(gè)請(qǐng)求與1個(gè)用戶(hù)同時(shí)向服務(wù)器發(fā)1000個(gè)請(qǐng)求對(duì)服務(wù)器造成的壓力是不一樣的,顯然是前者造成的壓力更大,原因是此時(shí)服務(wù)器網(wǎng)卡接收緩沖區(qū)中的請(qǐng)求同時(shí)有100個(gè)等待處理。
最大并發(fā)數(shù)是有一定利益前提的,是用戶(hù)和服務(wù)器各自期望利益的一個(gè)衡量點(diǎn)。一般是服務(wù)器保持了比較高的吞吐率同時(shí)用戶(hù)對(duì)等待時(shí)間比較滿(mǎn)意時(shí)的并發(fā)數(shù)即可定為最大并發(fā)數(shù)。
在并發(fā)用戶(hù)數(shù)較大的情況下,服務(wù)器采用什么樣的并發(fā)策略是影響最大并發(fā)數(shù)的關(guān)鍵。
用戶(hù)訪(fǎng)問(wèn)web站點(diǎn)通常是使用瀏覽器,而瀏覽器在下載一個(gè)網(wǎng)頁(yè)及網(wǎng)頁(yè)中的組件是采用多線(xiàn)程下載的。但其對(duì)同一域名下的URL并發(fā)下載數(shù)是有限制的,具體限制因?yàn)g覽器及其版本和http版本不同。
服務(wù)器支持的最大并發(fā)數(shù)具體到真實(shí)用戶(hù)并不是一對(duì)一的關(guān)系。一個(gè)真實(shí)的用戶(hù)可能給服務(wù)器帶來(lái)兩個(gè)或更多的并發(fā)用戶(hù)數(shù)的壓力。
從web服務(wù)器的角度看,實(shí)際并發(fā)用戶(hù)數(shù)可理解為服務(wù)器維護(hù)不同用戶(hù)的文件描述符總數(shù)即并發(fā)連接數(shù)。不是同時(shí)有多少用戶(hù),服務(wù)器就為其建立多少連接,服務(wù)器一般會(huì)限制同時(shí)服務(wù)的最多用戶(hù)數(shù)。
web服務(wù)器工作的本質(zhì)是以最快的速度將內(nèi)核緩沖區(qū)中的用戶(hù)請(qǐng)求數(shù)據(jù)拿回來(lái)并盡量盡快處理完這些請(qǐng)求,并將響應(yīng)數(shù)據(jù)放到發(fā)送數(shù)據(jù)的緩沖區(qū)中,再去處理下一撥請(qǐng)求,如此反復(fù)。
用戶(hù)平均請(qǐng)求等待時(shí)間用于衡量服務(wù)器在一定并發(fā)用戶(hù)數(shù)下對(duì)單個(gè)用戶(hù)的服務(wù)質(zhì)量。而服務(wù)器平均請(qǐng)求處理時(shí)間用于衡量服務(wù)器的整體服務(wù)質(zhì)量,它是吞吐率的倒數(shù)。如果并發(fā)策略得當(dāng),每個(gè)請(qǐng)求的平均處理時(shí)間可以減少。
并發(fā)策略的設(shè)計(jì)就是在服務(wù)器同時(shí)處理較多請(qǐng)求的時(shí)候合理協(xié)調(diào)并充分利用CPU和IO計(jì)算 ,使其在較大并發(fā)用戶(hù)數(shù)下保持較高的吞吐率。但并不存在一個(gè)對(duì)所有請(qǐng)求性質(zhì)都較高的并發(fā)策略。
3.2 CPU并發(fā)計(jì)算
服務(wù)器之所以可以同時(shí)處理多個(gè)請(qǐng)求,在于操作系統(tǒng)通過(guò)多執(zhí)行流體系設(shè)計(jì)多個(gè)任務(wù)可以輪流使用系統(tǒng)資源,包括CPU、內(nèi)存、IO等。
多執(zhí)行流的一般實(shí)現(xiàn)便是進(jìn)程,多進(jìn)程的好處可以對(duì)CPU時(shí)間的輪流使用,對(duì)CPU計(jì)算和IO操作重疊利用。這里的IO主要是指磁盤(pán)IO和網(wǎng)絡(luò)IO,對(duì)CPU而言,它們慢的可憐。大多數(shù)進(jìn)程的時(shí)間主要耗在IO上。
進(jìn)程的調(diào)度由內(nèi)核執(zhí)行,進(jìn)程的目的是擔(dān)當(dāng)分配資源的實(shí)體。每個(gè)進(jìn)程都有自己的內(nèi)存地址空間和生命周期。子進(jìn)程被父進(jìn)程創(chuàng)建后便把父進(jìn)程地址空間的所有數(shù)據(jù)復(fù)制到自己的內(nèi)存地址空間。完全繼承父進(jìn)程的上下文信息,它們之間可以互相通信,但不互相依賴(lài),無(wú)權(quán)干涉。
進(jìn)程的創(chuàng)建使用fork()系統(tǒng)調(diào)用,服務(wù)器頻繁地創(chuàng)建進(jìn)程會(huì)引起不小的性能開(kāi)銷(xiāo)。Linux 2.6對(duì)fork()進(jìn)行了優(yōu)化,減少了一些多余的內(nèi)存復(fù)制。
進(jìn)程的優(yōu)越性體現(xiàn)在其穩(wěn)定性和健壯性,其中一個(gè)進(jìn)程崩潰不會(huì)影響到另一個(gè)進(jìn)程。但采用大量進(jìn)程的web服務(wù)器(如:Apache prefork模型)在處理大量并發(fā)請(qǐng)求時(shí)其內(nèi)存開(kāi)銷(xiāo)將成為性能的瓶頸。
輕量級(jí)進(jìn)程由系統(tǒng)調(diào)用clone()來(lái)創(chuàng)建,由內(nèi)核管理,獨(dú)立存在,允許這些進(jìn)程共享數(shù)據(jù),輕量級(jí)進(jìn)程減少了內(nèi)存開(kāi)銷(xiāo),為多進(jìn)程應(yīng)用提供了數(shù)據(jù)共享,但其上下文切換開(kāi)銷(xiāo)還是避免不了。
一般多線(xiàn)程的管理在用戶(hù)態(tài)完成,線(xiàn)程切換的開(kāi)銷(xiāo)比輕量級(jí)進(jìn)程切換開(kāi)銷(xiāo)要小,但它在多CPU服務(wù)器中表現(xiàn)較差。
進(jìn)程調(diào)度器維護(hù)著一個(gè)可運(yùn)行隊(duì)列以及一個(gè)包括所有休眠和僵尸進(jìn)程的列表。進(jìn)程調(diào)度器的工作就是決定下一個(gè)運(yùn)行的進(jìn)程。如果隊(duì)列中有多個(gè)可運(yùn)行的進(jìn)程,此時(shí)進(jìn)程調(diào)度器可根據(jù)進(jìn)程的優(yōu)先級(jí)及其它策略進(jìn)行選擇。
CPU時(shí)間片的長(zhǎng)度要具體權(quán)衡,時(shí)間片太短,那么CPU在進(jìn)程切換上的時(shí)間浪費(fèi)就比較大,如果時(shí)間片太長(zhǎng),那么多任務(wù)實(shí)時(shí)性和交互性就無(wú)法做到保證。
系統(tǒng)負(fù)載越高代表CPU越忙,也就越無(wú)法很好地滿(mǎn)足所有進(jìn)程的需要。系統(tǒng)負(fù)載的計(jì)算是根據(jù)單位時(shí)間內(nèi)運(yùn)行隊(duì)列中就緒等待的進(jìn)程數(shù)平均值。當(dāng)運(yùn)行隊(duì)列中的就緒進(jìn)程不需要等待就可以立即得到CPU說(shuō)明系統(tǒng)負(fù)載比較低,系統(tǒng)響應(yīng)速度也就快。
查看系統(tǒng)負(fù)載可以通過(guò)cat /proc/loadavg、top、w等命令工具查看。
進(jìn)程的切換就是進(jìn)程調(diào)度器掛起正在運(yùn)行的進(jìn)程,恢復(fù)之前掛起的某個(gè)進(jìn)程。
每個(gè)進(jìn)程只能共享CPU寄存器,一個(gè)進(jìn)程被掛起的本質(zhì)就是將其在CPU寄存器中的數(shù)據(jù)取出來(lái)暫存到內(nèi)核堆棧中,恢復(fù)一個(gè)進(jìn)程的本質(zhì)就是將其數(shù)據(jù)重新載入到CPU寄存器中,其實(shí)這種硬件上下文切換的開(kāi)銷(xiāo)也是挺大的。
要服務(wù)器支持較大的并發(fā)數(shù),就要減少上下文切換的次數(shù),最簡(jiǎn)單地做法是減少進(jìn)程數(shù)目,盡量使用線(xiàn)程配合其它IO模型來(lái)設(shè)計(jì)并發(fā)策略。
除了關(guān)注CPU使用率外,還要關(guān)注IOWait,它是指CPU空閑并等待IO操作完成的時(shí)間比例。IOWait不能真實(shí)地代表IO操作的性能或工作量,它是衡量CPU性能的。即使IOWait為100%也不代表IO出現(xiàn)性能瓶頸,IOWait為0時(shí)IO也可能很忙。此時(shí),最好是測(cè)試磁盤(pán)IO和查看網(wǎng)絡(luò)IO的流量。
3.3 系統(tǒng)調(diào)用
進(jìn)程有用戶(hù)態(tài)和內(nèi)核態(tài)兩種運(yùn)行模式。進(jìn)程可以在這兩種模式中切換,存在一定的開(kāi)銷(xiāo)。進(jìn)程通常運(yùn)行在用戶(hù)態(tài),當(dāng)進(jìn)程需要對(duì)硬件操作的時(shí)候就要切換到內(nèi)核態(tài)。這兩種模式的分離是為了底層操作的安全性和簡(jiǎn)化開(kāi)發(fā)模型。所有進(jìn)程都必須通過(guò)內(nèi)核提供的系統(tǒng)調(diào)用來(lái)操作硬件。進(jìn)程從用戶(hù)態(tài)到內(nèi)核態(tài)存在一定的內(nèi)存空間切換,這種開(kāi)銷(xiāo)是比較昂貴的,應(yīng)盡量減少不必要的系統(tǒng)調(diào)用。
3.4 內(nèi)存分配
Web服務(wù)器在工作的過(guò)程中需要大量的內(nèi)存,這使得內(nèi)存的分配和釋放很重要。服務(wù)器處理成千上萬(wàn)的http請(qǐng)求,其內(nèi)存堆棧的分配和復(fù)制次數(shù)變得更加頻繁。
Apache在運(yùn)行時(shí)內(nèi)存使用量非常驚人,它一開(kāi)始就申請(qǐng)大量?jī)?nèi)存作內(nèi)存池,為防止以后頻繁的內(nèi)存再分配帶來(lái)的性能開(kāi)銷(xiāo),內(nèi)存池的使用使用Apache管理更安全,但內(nèi)存池的使用也沒(méi)有彌補(bǔ)其性能,其內(nèi)存池的釋放是在Apache關(guān)閉的時(shí)候。
Lighttpd使用單進(jìn)程模型,其內(nèi)存使用量比較小,同樣是使用單進(jìn)程的Nginx其內(nèi)存使用量更小,Nginx使用多線(xiàn)程處理請(qǐng)求,這些多線(xiàn)程可以共享內(nèi)存資源,它使用分階段按需分配內(nèi)存、及時(shí)釋放策略。
3.5 持久連接
持久連接是指一次TCP連接中持續(xù)處理多個(gè)請(qǐng)求而不斷開(kāi)連接。建立TCP連接操作的開(kāi)銷(xiāo)可不小,在允許的情況下,連接次數(shù)越小越有利于性能提升。
長(zhǎng)連接對(duì)于密集型的圖片或網(wǎng)頁(yè)等小數(shù)據(jù)量的請(qǐng)求有明顯的加速作用。
Http長(zhǎng)連接的實(shí)施需要瀏覽器和服務(wù)器的配合,缺一不可。
瀏覽器要支持http長(zhǎng)連接可以在http請(qǐng)求頭中加入:Connection: Keep-Alive,目前主流web服務(wù)器都默認(rèn)使用長(zhǎng)連接,除非顯式關(guān)閉。
對(duì)于長(zhǎng)連接的使用要注意長(zhǎng)連接的有效時(shí)間多長(zhǎng),即什么時(shí)候關(guān)閉長(zhǎng)連接,瀏覽器和服務(wù)器都有默認(rèn)的有效時(shí)間,也都可以設(shè)置有效時(shí)間,都可以主動(dòng)關(guān)閉,若兩者設(shè)置的時(shí)間長(zhǎng)度不一致,以短的為準(zhǔn)。例如:
請(qǐng)求:Connection:Keep-Alive
響應(yīng):Connection:Keep-Alive
Keep-Alive:timeout=5,max=100
持久連接的目的就是減少連接次數(shù),重用已有的連接通道,減少連接開(kāi)銷(xiāo)。
3.6 IO模型
IO有內(nèi)存IO、網(wǎng)絡(luò)IO和磁盤(pán)IO等。
可以使用RAID磁盤(pán)陣列來(lái)加速對(duì)磁盤(pán)IO的訪(fǎng)問(wèn),使用獨(dú)立網(wǎng)絡(luò)帶寬和高帶寬網(wǎng)絡(luò)適配器可以搞網(wǎng)絡(luò)IO速度,但I(xiàn)O操作都要由內(nèi)核系統(tǒng)調(diào)用完成,系統(tǒng)調(diào)用需要CPU調(diào)用,無(wú)疑存在CPU快和IO慢的不協(xié)調(diào)。
我們所關(guān)注的IO操作主要是網(wǎng)絡(luò)數(shù)據(jù)的發(fā)送、接收和磁盤(pán)文件的訪(fǎng)問(wèn)。不同IO模型的本質(zhì)在于CPU參與的方式。
DMA:直接內(nèi)存訪(fǎng)問(wèn)。即不需要通過(guò)CPU即可以進(jìn)行內(nèi)存到磁盤(pán)的數(shù)據(jù)交換。這樣就可降低對(duì)CPU的占有率,節(jié)省系統(tǒng)資源。
IO等待是不可避免的,既然有等待,就會(huì)有阻塞。這里的阻塞是指當(dāng)前發(fā)起請(qǐng)求的進(jìn)程IO被阻塞,并不是CPU被阻塞,CPU是沒(méi)有阻塞的,它只有拼命地計(jì)算。
同步阻塞IO是指當(dāng)前進(jìn)程調(diào)用某些IO操作的系統(tǒng)調(diào)用或庫(kù)函數(shù)時(shí),進(jìn)程便暫停下來(lái),等待IO操作完成后再繼續(xù)進(jìn)行,這種模型可以和多進(jìn)程結(jié)合起來(lái)有效利用CPU資源,但其代價(jià)就是多進(jìn)程的大內(nèi)存開(kāi)銷(xiāo)。這種模型的等待時(shí)間包括等待數(shù)據(jù)的就緒和等待數(shù)據(jù)的復(fù)制。
同步非阻塞IO是指調(diào)用不會(huì)等待數(shù)據(jù)的就緒,當(dāng)沒(méi)數(shù)據(jù)可讀或可寫(xiě)時(shí)立即告訴進(jìn)程,讓其函數(shù)及時(shí)返回。通過(guò)反復(fù)輪詢(xún)來(lái)嘗試數(shù)據(jù)是否就緒,防止進(jìn)程被阻塞,最大的一個(gè)好處就是可以在一個(gè)進(jìn)程內(nèi)同時(shí)處理多個(gè)IO操作。但是反復(fù)輪詢(xún)會(huì)大量占用CPU時(shí)間,使得進(jìn)程處于忙碌等待狀態(tài)。非阻塞IO只對(duì)網(wǎng)絡(luò)IO有效,對(duì)磁盤(pán)IO無(wú)效。
多路IO就緒通知允許進(jìn)程通過(guò)一種方法同時(shí)監(jiān)視所有文件描述符,并可以快速獲得所有就緒的文件描述符,然后只針對(duì)這些文件描述符進(jìn)行數(shù)據(jù)訪(fǎng)問(wèn)。當(dāng)然,要注意,這種模型在數(shù)據(jù)訪(fǎng)問(wèn)時(shí)仍然要采用阻塞或非阻塞方式進(jìn)行。
select:通過(guò)一個(gè)select()系統(tǒng)調(diào)用來(lái)監(jiān)視并返回就緒的文件描述符,從而對(duì)這些文件描述符進(jìn)行后續(xù)的讀寫(xiě)。幾乎所有的平臺(tái)都支持這種方式,可以跨平臺(tái),但它的缺點(diǎn)是單個(gè)進(jìn)程可監(jiān)視的文件描述符數(shù)量有最大限制,Linux上一般為1024,它對(duì)所有socket進(jìn)行一次性?huà)呙枰泊嬖陂_(kāi)銷(xiāo)。
poll:與select沒(méi)有本質(zhì)區(qū)別,只是poll沒(méi)有最大文件描述符數(shù)量限制。它的缺點(diǎn)也是將大理文件描述符的數(shù)組在用戶(hù)態(tài)和內(nèi)核態(tài)來(lái)回復(fù)制,而不管文件描述符是否就緒,開(kāi)銷(xiāo)會(huì)成線(xiàn)性增長(zhǎng)。
epoll:Linux2.6才出現(xiàn),具有其它方式的一切優(yōu)點(diǎn),是Linux2.6下性能最好的多路IO就緒通知方法。它基于事件的就緒通知方式。
kqueue:性能和epoll差不多,它是FreeBSD下的,但它的API在許多平臺(tái)下不支持。
內(nèi)存映射是指將內(nèi)存中某塊地址空間和我們指定的磁盤(pán)文件相關(guān)聯(lián),從而把對(duì)這塊內(nèi)存的訪(fǎng)問(wèn)轉(zhuǎn)換為對(duì)磁盤(pán)文件的訪(fǎng)問(wèn)。內(nèi)存映射可以提高磁盤(pán)IO性能,像訪(fǎng)問(wèn)內(nèi)存一樣地訪(fǎng)問(wèn)磁盤(pán)文件。有兩種內(nèi)存映射,共享型和私有型。共享型是指對(duì)任何內(nèi)存的寫(xiě)操作都同步到磁盤(pán)文件,而所有映射同一個(gè)文件的進(jìn)程都共享任意一個(gè)進(jìn)程對(duì)映射內(nèi)存的修改。私有型是指映射的文件只能是只讀文件,不可以將內(nèi)存的寫(xiě)同步到文件,多個(gè)進(jìn)程不共享修改。顯然,共享型的內(nèi)存映射效率偏低。
直接IO就是指繞過(guò)內(nèi)核緩沖區(qū),打開(kāi)的文件可直接訪(fǎng)問(wèn),避免CPU和內(nèi)存的多余時(shí)間開(kāi)銷(xiāo)。
sendfile系統(tǒng)調(diào)用可將磁盤(pán)文件的特定部分直接送到客戶(hù)端的Socket的描述符,加快靜態(tài)文件的請(qǐng)求速度,減少CPU和內(nèi)存的開(kāi)銷(xiāo)。
阻塞和非阻塞是指當(dāng)進(jìn)程訪(fǎng)問(wèn)的數(shù)據(jù)尚未就緒,進(jìn)程是否等待即是立即返回還是繼續(xù)等待。同步是指主動(dòng)請(qǐng)求并等待IO操作完成,當(dāng)數(shù)據(jù)就緒后讀寫(xiě)時(shí)必須阻塞。異步是指主動(dòng)請(qǐng)求數(shù)據(jù)后可以繼續(xù)處理其它任務(wù),隨便等待IO操作完成的通知,即讀寫(xiě)時(shí)進(jìn)程不阻塞。
3.7 服務(wù)器并發(fā)策略
設(shè)計(jì)并發(fā)策略的目的就是就是讓IO操作和CPU計(jì)算盡量重疊進(jìn)行。一方面要讓CPU在IO等待不要空閑,另一方面要讓CPU在IO調(diào)度上盡量花最少的時(shí)間。
(1)一個(gè)進(jìn)程處理一個(gè)連接,非阻塞IO
這樣會(huì)存在多個(gè)并發(fā)請(qǐng)求同時(shí)到達(dá)時(shí),服務(wù)器必然要準(zhǔn)備多個(gè)進(jìn)程來(lái)處理請(qǐng)求。這種策略典型的例子就是Apache的fork和prefork模式。對(duì)于并發(fā)數(shù)不高的站點(diǎn)同時(shí)依賴(lài)Apache其它功能時(shí)的應(yīng)用選擇Apache還是可以的。
(2)一個(gè)線(xiàn)程處理一個(gè)連接,非阻塞IO
這種方式允許在一個(gè)進(jìn)程中通過(guò)多個(gè)線(xiàn)程來(lái)處理多個(gè)連接,一個(gè)線(xiàn)程處理一個(gè)連接。Apache的worker模式就是這種典型例子,使其可支持更多的并發(fā)連接。不過(guò)這種模式的總體性能還不如prefork,所以一般不選用worker模式。
(3)一個(gè)進(jìn)程處理多個(gè)連接,非阻塞IO
適用的前提條件就是多路IO就緒通知的應(yīng)用。這種情況下,將處理多個(gè)連接的進(jìn)程叫做worker進(jìn)程或服務(wù)進(jìn)程。worker的數(shù)量可以配置,如Nginx中的worker_processes 4
(4)一個(gè)線(xiàn)程處理多個(gè)連接,異步IO
即使有高性能的多路IO就緒通知,但磁盤(pán)IO的等待還是無(wú)法避免的。更加高效的方法是對(duì)磁盤(pán)文件使用異步IO,目前很少有Web服務(wù)器真正意義上支持這種異步IO。
作者:tujiyue
來(lái)源:CSDN
原文:https://blog.csdn.net/tujiyue/article/details/7027134
版權(quán)聲明:本文為博主原創(chuàng)文章,轉(zhuǎn)載請(qǐng)附上博文鏈接!