服務器併發處理能力

序:web

    這裏指的服務器是指提供HTTP服務的服務器,人們一般衡量一臺web服務器能力的大小爲其在單位時間內能處理的請求數的多少。數組

3.1 吞吐率瀏覽器

    Web服務器的吞吐率是指其單位時間內所能處理的請求數。更關心的是服務器併發處理能力的上限即最大吞吐率。安全

    Web服務器在實際工做中,其處理的Http請求包括對不少不一樣資源的請求即請求的url不同。正由於這種請求性質的不一樣,Web服務器併發能力的強弱關鍵在於如何針對不一樣的請求性質設計不一樣的併發策略。有時候一臺Web服務器要同時處理許多不一樣性質的請求,在一程度上使得Web服務器性能沒法發揮。服務器

    併發用戶數爲某一時刻同時向服務器發送請求的用戶數。注意,100個用戶同時向服務器各發10個請求與1個用戶同時向服務器發1000個請求對服務器形成的壓力是不同的,顯然是前者形成的壓力更大,緣由是此時服務器網卡接收緩衝區中的請求同時有100個等待處理。網絡

    最大併發數是有必定利益前提的,是用戶和服務器各自指望利益的一個衡量點。通常是服務器保持了比較高的吞吐率同時用戶對等待時間比較滿意時的併發數便可定爲最大併發數。多線程

    在併發用戶數較大的狀況下,服務器採用什麼樣的併發策略是影響最大併發數的關鍵。併發

    用戶訪問web站點一般是使用瀏覽器,而瀏覽器在下載一個網頁及網頁中的組件是採用多線程下載的。但其對同一域名下的URL併發下載數是有限制的,具體限制因瀏覽器及其版本和http版本不一樣。異步

    服務器支持的最大併發數具體到真實用戶並非一對一的關係。一個真實的用戶可能給服務器帶來兩個或更多的併發用戶數的壓力。socket

    從web服務器的角度看,實際併發用戶數可理解爲服務器維護不一樣用戶的文件描述符總數即併發鏈接數。不是同時有多少用戶,服務器就爲其創建多少鏈接,服務器通常會限制同時服務的最多用戶數。

    web服務器工做的本質是以最快的速度將內核緩衝區中的用戶請求數據拿回來並儘可能儘快處理完這些請求,並將響應數據放到發送數據的緩衝區中,再去處理下一撥請求,如此反覆。

    用戶平均請求等待時間用於衡量服務器在必定併發用戶數下對單個用戶的服務質量。而服務器平均請求處理時間用於衡量服務器的總體服務質量,它是吞吐率的倒數。若是併發策略得當,每一個請求的平均處理時間能夠減小。

    併發策略的設計就是在服務器同時處理較多請求的時候合理協調並充分利用CPU和IO計算 ,使其在較大併發用戶數下保持較高的吞吐率。但並不存在一個對全部請求性質都較高的併發策略。

3.2 CPU併發計算

    服務器之因此能夠同時處理多個請求,在於操做系統經過多執行流體系設計多個任務能夠輪流使用系統資源,包括CPU、內存、IO等。

    多執行流的通常實現即是進程,多進程的好處能夠對CPU時間的輪流使用,對CPU計算和IO操做重疊利用。這裏的IO主要是指磁盤IO和網絡IO,對CPU而言,它們慢的可憐。大多數進程的時間主要耗在IO上。

    進程的調度由內核執行,進程的目的是擔當分配資源的實體。每一個進程都有本身的內存地址空間和生命週期。子進程被父進程建立後便把父進程地址空間的全部數據複製到本身的內存地址空間。徹底繼承父進程的上下文信息,它們之間能夠互相通訊,但不互相依賴,無權干涉。

    進程的建立使用fork()系統調用,服務器頻繁地建立進程會引發不小的性能開銷。Linux 2.6對fork()進行了優化,減小了一些多餘的內存複製。

    進程的優越性體如今其穩定性和健壯性,其中一個進程崩潰不會影響到另外一個進程。但採用大量進程的web服務器(如:Apache prefork模型)在處理大量併發請求時其內存開銷將成爲性能的瓶頸。

    輕量級進程由系統調用clone()來建立,由內核管理,獨立存在,容許這些進程共享數據,輕量級進程減小了內存開銷,爲多進程應用提供了數據共享,但其上下文切換開銷仍是避免不了。

    通常多線程的管理在用戶態完成,線程切換的開銷比輕量級進程切換開銷要小,但它在多CPU服務器中表現較差。

    進程調度器維護着一個可運行隊列以及一個包括全部休眠和殭屍進程的列表。進程調度器的工做就是決定下一個運行的進程。若是隊列中有多個可運行的進程,此時進程調度器可根據進程的優先級及其它策略進行選擇。

    CPU時間片的長度要具體權衡,時間片過短,那麼CPU在進程切換上的時間浪費就比較大,若是時間片太長,那麼多任務實時性和交互性就沒法作到保證。

    系統負載越高表明CPU越忙,也就越沒法很好地知足全部進程的須要。系統負載的計算是根據單位時間內運行隊列中就緒等待的進程數平均值。當運行隊列中的就緒進程不須要等待就能夠當即獲得CPU說明系統負載比較低,系統響應速度也就快。

    查看系統負載能夠經過cat /proc/loadavg、top、w等命令工具查看。

    進程的切換就是進程調度器掛起正在運行的進程,恢復以前掛起的某個進程。

    每一個進程只能共享CPU寄存器,一個進程被掛起的本質就是將其在CPU寄存器中的數據取出來暫存到內核堆棧中,恢復一個進程的本質就是將其數據從新載入到CPU寄存器中,其實這種硬件上下文切換的開銷也是挺大的。

    要服務器支持較大的併發數,就要減小上下文切換的次數,最簡單地作法是減小進程數目,儘可能使用線程配合其它IO模型來設計併發策略。

    除了關注CPU使用率外,還要關注IOWait,它是指CPU空閒並等待IO操做完成的時間比例。IOWait不能真實地表明IO操做的性能或工做量,它是衡量CPU性能的。即便IOWait爲100%也不表明IO出現性能瓶頸,IOWait爲0時IO也可能很忙。此時,最好是測試磁盤IO和查看網絡IO的流量。

3.3 系統調用

    進程有用戶態和內核態兩種運行模式。進程能夠在這兩種模式中切換,存在必定的開銷。進程一般運行在用戶態,當進程須要對硬件操做的時候就要切換到內核態。這兩種模式的分離是爲了底層操做的安全性和簡化開發模型。全部進程都必須經過內核提供的系統調用來操做硬件。進程從用戶態到內核態存在必定的內存空間切換,這種開銷是比較昂貴的,應儘可能減小沒必要要的系統調用。

3.4 內存分配

    Web服務器在工做的過程當中須要大量的內存,這使得內存的分配和釋放很重要。服務器處理成千上萬的http請求,其內存堆棧的分配和複製次數變得更加頻繁。

    Apache在運行時內存使用量很是驚人,它一開始就申請大量內存做內存池,爲防止之後頻繁的內存再分配帶來的性能開銷,內存池的使用使用Apache管理更安全,但內存池的使用也沒有彌補其性能,其內存池的釋放是在Apache關閉的時候。

    Lighttpd使用單進程模型,其內存使用量比較小,一樣是使用單進程的Nginx其內存使用量更小,Nginx使用多線程處理請求,這些多線程能夠共享內存資源,它使用分階段按需分配內存、及時釋放策略。

3.5 持久鏈接

    持久鏈接是指一次TCP鏈接中持續處理多個請求而不斷開鏈接。創建TCP鏈接操做的開銷可不小,在容許的狀況下,鏈接次數越小越有利於性能提高。

    長鏈接對於密集型的圖片或網頁等小數據量的請求有明顯的加速做用。

    Http長鏈接的實施須要瀏覽器和服務器的配合,缺一不可。

    瀏覽器要支持http長鏈接能夠在http請求頭中加入:Connection: Keep-Alive,目前主流web服務器都默認使用長鏈接,除非顯式關閉。

    對於長鏈接的使用要注意長鏈接的有效時間多長,即何時關閉長鏈接,瀏覽器和服務器都有默認的有效時間,也均可以設置有效時間,均可以主動關閉,若二者設置的時間長度不一致,以短的爲準。例如:

    請求:Connection:Keep-Alive

     響應:Connection:Keep-Alive

                Keep-Alive:timeout=5,max=100

    持久鏈接的目的就是減小鏈接次數,重用已有的鏈接通道,減小鏈接開銷。

3.6 IO模型

    IO有內存IO、網絡IO和磁盤IO等。

    可使用RAID磁盤陣列來加速對磁盤IO的訪問,使用獨立網絡帶寬和高帶寬網絡適配器能夠搞網絡IO速度,但IO操做都要由內核系統調用完成,系統調用須要CPU調用,無疑存在CPU快和IO慢的不協調。

    咱們所關注的IO操做主要是網絡數據的發送、接收和磁盤文件的訪問。不一樣IO模型的本質在於CPU參與的方式。

    DMA:直接內存訪問。即不須要經過CPU便可以進行內存到磁盤的數據交換。這樣就可下降對CPU的佔有率,節省系統資源。

    IO等待是不可避免的,既然有等待,就會有阻塞。這裏的阻塞是指當前發起請求的進程IO被阻塞,並非CPU被阻塞,CPU是沒有阻塞的,它只有拼命地計算。

    同步阻塞IO是指當前進程調用某些IO操做的系統調用或庫函數時,進程便暫停下來,等待IO操做完成後再繼續進行,這種模型能夠和多進程結合起來有效利用CPU資源,但其代價就是多進程的大內存開銷。這種模型的等待時間包括等待數據的就緒和等待數據的複製。

    同步非阻塞IO是指調用不會等待數據的就緒,當沒數據可讀或可寫時當即告訴進程,讓其函數及時返回。經過反覆輪詢來嘗試數據是否就緒,防止進程被阻塞,最大的一個好處就是能夠在一個進程內同時處理多個IO操做。可是反覆輪詢會大量佔用CPU時間,使得進程處於忙碌等待狀態。非阻塞IO只對網絡IO有效,對磁盤IO無效。

    多路IO就緒通知容許進程經過一種方法同時監視全部文件描述符,並能夠快速得到全部就緒的文件描述符,而後只針對這些文件描述符進行數據訪問。固然,要注意,這種模型在數據訪問時仍然要採用阻塞或非阻塞方式進行。

    select:經過一個select()系統調用來監視並返回就緒的文件描述符,從而對這些文件描述符進行後續的讀寫。幾乎全部的平臺都支持這種方式,能夠跨平臺,但它的缺點是單個進程可監視的文件描述符數量有最大限制,Linux上通常爲1024,它對全部socket進行一次性掃描也存在開銷。

    poll:與select沒有本質區別,只是poll沒有最大文件描述符數量限制。它的缺點也是將大理文件描述符的數組在用戶態和內核態來回複製,而無論文件描述符是否就緒,開銷會成線性增加。

    epoll:Linux2.6纔出現,具備其它方式的一切優勢,是Linux2.6下性能最好的多路IO就緒通知方法。它基於事件的就緒通知方式。

    kqueue:性能和epoll差很少,它是FreeBSD下的,但它的API在許多平臺下不支持。

    內存映射是指將內存中某塊地址空間和咱們指定的磁盤文件相關聯,從而把對這塊內存的訪問轉換爲對磁盤文件的訪問。內存映射能夠提升磁盤IO性能,像訪問內存同樣地訪問磁盤文件。有兩種內存映射,共享型和私有型。共享型是指對任何內存的寫操做都同步到磁盤文件,而全部映射同一個文件的進程都共享任意一個進程對映射內存的修改。私有型是指映射的文件只能是隻讀文件,不能夠將內存的寫同步到文件,多個進程不共享修改。顯然,共享型的內存映射效率偏低。

    直接IO就是指繞過內核緩衝區,打開的文件可直接訪問,避免CPU和內存的多餘時間開銷。

    sendfile系統調用可將磁盤文件的特定部分直接送到客戶端的Socket的描述符,加快靜態文件的請求速度,減小CPU和內存的開銷。

    阻塞和非阻塞是指當進程訪問的數據還沒有就緒,進程是否等待便是當即返回仍是繼續等待。同步是指主動請求並等待IO操做完成,當數據就緒後讀寫時必須阻塞。異步是指主動請求數據後能夠繼續處理其它任務,隨便等待IO操做完成的通知,即讀寫時進程不阻塞。

3.7 服務器併發策略

    設計併發策略的目的就是就是讓IO操做和CPU計算儘可能重疊進行。一方面要讓CPU在IO等待不要空閒,另外一方面要讓CPU在IO調度上儘可能花最少的時間。

    (1)一個進程處理一個鏈接,非阻塞IO

        這樣會存在多個併發請求同時到達時,服務器必然要準備多個進程來處理請求。這種策略典型的例子就是Apache的fork和prefork模式。對於併發數不高的站點同時依賴Apache其它功能時的應用選擇Apache仍是能夠的。

    (2)一個線程處理一個鏈接,非阻塞IO

        這種方式容許在一個進程中經過多個線程來處理多個鏈接,一個線程處理一個鏈接。Apache的worker模式就是這種典型例子,使其可支持更多的併發鏈接。不過這種模式的整體性能還不如prefork,因此通常不選用worker模式。

    (3)一個進程處理多個鏈接,非阻塞IO

        適用的前提條件就是多路IO就緒通知的應用。這種狀況下,將處理多個鏈接的進程叫作worker進程或服務進程。worker的數量能夠配置,如Nginx中的worker_processes 4

    (4)一個線程處理多個鏈接,異步IO

        即便有高性能的多路IO就緒通知,但磁盤IO的等待仍是沒法避免的。更加高效的方法是對磁盤文件使用異步IO,目前不多有Web服務器真正意義上支持這種異步IO。

相關文章
相關標籤/搜索