天天看點

怎樣提高伺服器并發處理能力?

什麼是伺服器并發處理能力?

一台伺服器在機關時間裡能處理的請求越多,伺服器的能力越高,也就是伺服器并發處理能力越強。

伺服器的本質工作就是,争取以最快的速度将核心緩沖區中的使用者請求資料一個不剩地都拿出來,然後盡快處理,再将響應資料放到一塊又能夠與發送資料的緩沖區中,接着處理下一撥請求。

有什麼方法衡量伺服器并發處理能力?

一,吞吐率

量化名額:吞吐率,機關時間裡伺服器處理的最大請求數,機關req/s。

再深入一些,HTTP請求通常是對不同資源的請求,也就是請求不同的URL,有的是請求圖檔,有的是擷取動态内容,有的是靜态頁面,顯然這些請求所花費的時間各不相同,而這些請求再不同時間的組成比例又是不确定的,是以實際情況下的吞吐率是非常複雜的。

正因為這些請求的性質不同,是以伺服器并發能力強弱關鍵在于如何正對不同的請求性質來設計最優并發政策。如一台伺服器處理諸多不同性質的請求,在一定程度上使得伺服器的性能無法充分發揮。而并發政策的設計就是在伺服器同時處理較多請求時,合理協調和充分利用CPU計算和I/O操作,使其在較大并發使用者數的情況下提供較高吞吐率。

另外,實際上多少使用者同時發來請求并不是伺服器所能決定的,一旦實際并發使用者數過多,則勢必影響站點品質。是以得出最大并發使用者數的意義,在于了解伺服器的承載能力,并且結合使用者規模考慮适當的擴充方案。在考慮使用者模型時,使用者通路 web站點時通常使用浏覽器,浏覽器對于同一域名下URL的并發下載下傳是多線程的,不過有最大限制的,是以前面說到的最大并發數,具體到真實的使用者,可能不是一對一的關系。而從伺服器角度,實際并發使用者數的可以了解為伺服器目前維護的代表不同使用者的檔案描述符總數,也就是并發連接配接數。伺服器一般會限制同時服務的最多使用者數,比如apache的MaxClents參數。這裡再深入一下,對于伺服器來說,伺服器希望支援高吞吐率,對于使用者來說,使用者隻希望等待最少的時間,顯然,雙方不能滿足,是以雙方利益的平衡點,就是我們希望的最大并發使用者數。

二,壓力測試

有一個原理一定要先搞清楚,假如100個使用者同時向伺服器分别進行10個請求,與1個使用者向伺服器連續進行1000次請求,對伺服器的壓力是一樣嗎?實際上是不一樣的,因對每一個使用者,連續發送請求實際上是指發送一個請求并接收到響應資料後再發送下一個請求。這樣對于1個使用者向伺服器連續進行1000次請求, 任何時刻伺服器的網卡接收緩沖區中隻有1個請求,而對于100個使用者同時向伺服器分别進行10個請求,伺服器的網卡接收緩沖區最多有100個等待處理的請求,顯然這時的伺服器壓力更大。

壓力測試前提考慮的條件:

并發使用者數

總請求數

請求資源描述

并發使用者數是指某一時刻同時向伺服器發送請求的使用者總數。

壓力測試中關系的時間有細分以下2種,

使用者平均請求等待時間(這裡暫不把資料在網絡的傳輸時間,還有使用者PC本地的計算時間計算入内)

伺服器平均請求處理時間

使用者平均請求等待時間主要用于衡量伺服器在一定并發使用者數下,單個使用者的服務品質;而伺服器平均請求處理時間就是吞吐率的倒數,一般來說,使用者平均請求等待時間=伺服器平均請求處理時間*并發使用者數

怎麼提高伺服器的并發處理能力呢?

一,提高CPU并發計算能力

伺服器之是以可以同時處理多個請求,在于作業系統通過多執行流體系設計使得多個任務可以輪流使用系統資源,這些資源包括CPU,記憶體以及I/O. 這裡的I/O主要指磁盤I/O, 和網絡I/O

多程序&多線程

多執行流的一般實作就是程序。多程序的好處不僅在于CPU時間的輪流使用,還在于對CPU計算和I/O操作進行很好的重疊利用,這裡的I/O主要指磁盤I/O和網絡I/O. 實際上,大多數程序的時間主要消耗在I/O操作上,現代計算機的DMA技術可以讓CPU不參與I/O操作的全過程,比如程序通過系統調用,使得CPU向網卡或者磁盤等I/O裝置發出指令,然後程序被挂起,釋放出CPU資源,等待I/O裝置完成工作後通過中斷來通知程序重新就緒。對于單任務而言,CPU大部分時間空閑,這時候多程序的作用尤為重要。

而且程序的優越性還在其互相獨立帶來的穩定性和健壯性方面。

程序的缺點: 每個程序都有自己的獨立空間和生命周期。當子程序被父程序建立後,便将父程序位址空間的所有資料複制到自己的位址空間中,完全繼承父程序的上下文資訊。程序的建立使用fork()系統調用,還是有一定的開銷的,這個開銷若太頻繁,其可能成為影響性能的主要因素。

那是否越多程序數越好呢? 請看下面讨論:

減少程序切換

程序擁有獨立的記憶體空間,每個程序都隻能共享CPU寄存器。一個程序被挂起的本質是将它在CPU寄存器中的資料拿出來暫存在記憶體态堆棧着那個,而一個程序恢複工作的本質就是把它的資料重新裝入CPU寄存器,這段裝入和移出的資料稱為“硬體上下文”,除此之外,程序上下文還包含程序允許所需的一切狀态資訊。

當硬體上下文頻繁裝入和移出時,所消耗的時間是非常可觀的。可用Nmon工具監視伺服器每秒的上下文切換次數。為了盡量減少上下文切換次數,最簡單的做法就是減少程序數,盡量使用線程并配合其它I/O模型來設計并發政策。還可以考慮使用程序綁定CPU技術,增加CPU緩存的命中率。若程序不斷在各CPU上切換,這樣舊的CPU緩存就會失效。

減少使用不必要的鎖

伺服器處理大量并發請求時,多個請求處理任務時存在一些資源搶占競争,這時一般采用“鎖”機制來控制資源的占用,當一個任務占用資源時,我們鎖住資源,這時其它任務都在等待鎖的釋放,這個現象稱為鎖競争。

通過鎖競争的本質,我們要意識到盡量減少并發請求對于共享資源的競争。比如在允許情況下關閉伺服器通路日志,這可以大大減少在鎖等待時的延遲時間。要最大程度減少無辜的等待時間。這裡說下無鎖程式設計,就是由核心完成這個鎖機制,主要是使用原子操作替代鎖來實作對共享資源的通路保護 ,使用原子操作時,在進行實際的寫操作時,使用了lock指令,這樣就可以阻止其他任務寫這塊記憶體,避免出現資料競争現象。原子操作速度比鎖快,一般要快一倍以上。

例如fwrite(), fopen(),其是使用append方式寫檔案,其原理就是使用了無鎖程式設計,無鎖程式設計的複雜度高,但是效率快,而且發生死鎖機率低。

其它

除了上述所說,要優化伺服器的并發處理能力,還要考慮程序優先級(可由程序決定),程序排程器會動态調整運作隊列中程序的優先級,通過top觀察程序的PR值。

還要關注系統負載,可在任何時刻檢視/proc/loadavg, top中的load average也可看出

還要關注CPU使用率,除了使用者空間和核心空間的CPU使用率以外,還要關注I/O wait,它是指CPU空閑并且等待I/O操作完成的時間比例。(top中檢視wa的值)

二,考慮系統調用

程序若運作在使用者态,這時可使用CPU和記憶體來完成一些任務,而當程序需要對硬體外設進行操作的時候(如讀取磁盤檔案,發送網絡資料等),就必須切換到核心态,這時它擁有更多的權力來操縱整個計算機。

而系統調用涉及程序從使用者态到核心态的切換,導緻一定的記憶體交換,這也是一定程度上的上下文切換,是以系統調用的開銷通常認為比較昂貴的。

是以要減少不必要的系統調用,也是伺服器性能優化的一個方面。例如在apache中,修改httpd.conf檔案,可以減少對檔案路徑中各級目錄下檢測是否存在.htacess檔案這個open()系統調用;還可以修改httpd.conf檔案來減少多餘的gettimeofday()系統調用。

但有時若使用一些簡單的系統調用能代替大量的邏輯運算,這樣反而使用系統調用更能優化性能。

三,考慮減少記憶體配置設定和釋放

伺服器的工作過程中,需要大量的記憶體,使得記憶體的配置設定和釋放工作尤為重要。

可以通過改善資料結構和算法複制度來适當減少中間臨時變量的記憶體配置設定及資料複制時間,而伺服器本身也使用了各自的政策來提高效率。

例如Apache,在運作開始時一次申請大片的記憶體作為記憶體池,若随後需要時就在記憶體池中直接擷取,不需要再次配置設定,避免了頻繁的記憶體配置設定和釋放引起的記憶體整理時間。再如Nginx使用多線程來處理請求,使得多個線程之間可以共享記憶體資源,進而令它的記憶體總體使用量大大減少,另外,nginx分階段的記憶體配置設定政策,按需配置設定,及時釋放,使得記憶體使用量保持在很小的數量範圍。

順便說下 Linux程序的位址空間分段

1、棧(存放着局部變量和函數參數等資料),向下生長 (可讀可寫可執行)

2、堆(給動态配置設定記憶體是使用),向上生長 (可讀可寫可執行)

3、資料段(儲存全局資料和靜态資料) (可讀可寫不可執行)

4、代碼段(儲存代碼) (可讀可執行不可寫)

還可以考慮使用共享記憶體。共享記憶體指在多處理器的計算機系統中,可以被不同中央處理器(CPU)通路的大容量記憶體,也可以由不同程序共享,是非常快的程序通信方式。但是使用

買域名平台

共享記憶體也有不好的地方,就是對于多機器時資料不好統一。

shell指令ipcs可用來顯示系統下共享記憶體的狀态,函數shmget可以建立或打開一塊共享記憶體區,函數shmat将一個存在的共享記憶體段連接配接到本程序空間, 函數shmctl可以對共享記憶體段進行多種操作,函數shmdt函數分離該共享記憶體

四,考慮使用持久連接配接

持久連接配接也為長連接配接,它本身是TCP通信的一種普通方式,即在一次TCP連接配接中持續發送多分資料而不斷開連接配接,與它相反的方式稱為短連接配接,也就是建立連接配接後發送一份資料就斷開,然後再次建立連接配接發送下一份資料, 周而複始。是否采用持久連接配接,完全取決于應用特點。從性能角度看,建立TCP連接配接的操作本身是一項不小的開銷,在允許的情況下,連接配接次數越少,越有利于性能的提升; 尤其對于密集型的圖檔或網頁等小資料請求處理有明顯的加速所用。

HTTP長連接配接需要浏覽器和web伺服器的共同協作,目前浏覽器普遍支援長連接配接,表現在其發出的HTTP請求資料頭中包含關于長連接配接的聲明,如下:Connection: Keep-Alive

主流的web伺服器都支援長連接配接,比如apache中,可以用KeepAlive off關閉長連接配接。

對于長連接配接的有效使用,還有關鍵一點在于長連接配接逾時時間的設定,即長連接配接在什麼時候關閉嗎?Apache的預設設定為5s, 若這個時間設定過長,則可能導緻資源無效占有,維持大量空閑程序,影響伺服器性能。

五,改進I/O 模型

I/O操作根據裝置的不同分為很多類型,比如記憶體I/O, 網絡I/O, 磁盤I/O. 對于網絡I/O和磁盤I/O, 它們的速度要慢很多,盡管使用RAID磁盤陣列可通過并行磁盤磁盤來加快磁盤I/O速度,購買大連獨享網絡帶寬以及使用高帶寬網絡擴充卡可以提高網絡i/O的速度。但這些I/O操作需要核心系統調用來完成,這些需要CPU來排程,這使得CPU不得不浪費寶貴的時間來等待慢速I/O操作我們希望讓CPU足夠少的時間在i/O操作的排程上,如何讓高速的CPU和慢速的I/O裝置更好地協調工作,是現代計算機一直探讨的話題。各種I/O模型的本質差別在于CPU的參與方式。

  1. DMA技術I/O裝置和記憶體之間的資料傳輸方式由DMA控制器完成。

在DMA模式下,CPU隻需向DMA下達指令,讓DMA控制器來處理資料的傳送,這樣可以大大節省系統資源。

  1. 異步I/O

異步I/O指主動請求資料後便可以繼續處理其它任務,随後等待I/O操作的通知,這樣程序在資料讀寫時不發生阻塞。而同步則在資料就緒後在讀寫時必須阻塞。異步I/O是非阻塞的,當函數傳回時,真正的I/O傳輸還沒開始,這讓CPU處理和I/O操作達到很好的重疊。順便說說同步阻塞I/O的缺點,其雖然可以和多程序有效利用CPU資源,但代價是占用了大量的記憶體開銷。而同步非阻塞I/O需要程序執行多次輪訓檢視資料是否就緒,花費了大量的CPU時間。

  1. 改進多路I/O就緒通知政策

epoll伺服器同時處理大量的檔案描述符是必不可少的,若采用同步非阻塞I/O模型,若同時接收TCP連接配接的資料,就必須輪流對每個socket調用接收資料的方法,不管這些socket有沒有可接收的資料,都要詢問一次,假如大部分socket并沒有資料可以接收,那麼程序便會浪費很多CPU時間用于檢查這些socket.有沒有可以接收的資料, 多路I/O就緒通知的出現,提供了對大量檔案描述符就緒檢查的高性能方案,它允許程序通過一種方法同時監視所有檔案描述符,并可以快速獲得所有就緒的檔案描述符,然後隻針對這些檔案描述符進行資料通路。

下面詳細介紹被公認為linux 2.6下性能最好的多路I/O就緒通知方法epoll., 幾乎具備select, poll, /dev/poll等模型的全部優點。

epoll可以同時支援水準觸發和邊緣觸發,理論上邊緣觸發性能更高,但是代碼實作複雜,因為任何意外的丢失事件都會造成請求處理錯誤。

epoll主要有2大改進:

epoll隻告知就緒的檔案描述符,而且當調用epoll_wait()獲得檔案描述符時,傳回并不是實際的描述符,而是一個代表就緒描述符數量的值,然後隻需去epoll指定的一個數組中依次取得相應數量的檔案描述符即可,這裡使用了記憶體映射(mmap)技術,這樣徹底省掉了這些檔案描述符在系統調用時複制的開銷。

epoll采用基于事件的就緒通知方式。其事先通過epollctrl()注冊每一個檔案描述符,一旦某個檔案描述符就緒時,核心會采用類似callback的回調機制,當程序調用epollwait()時得到通知

  1. Sendfile

大多數時候,我們都向伺服器請求靜态檔案,比如圖檔,樣式表等,在處理這些請求時,磁盤檔案的資料先經過核心緩沖區,然後到使用者記憶體空間,不需經過任何處理,其又被送到網卡對應的核心緩沖區,接着再被送入網卡進行發送。

Linux提供sendfile()系統調用,可以講磁盤檔案的特定部分直接傳送到代表用戶端的socket描述符,加快了靜态檔案的請求速度,同時減少CPU和記憶體的開銷。

适用場景:對于請求較小的靜态檔案,sendfile發揮的作用不那麼明顯,因發送資料的環節在整個過程中所占時間的比例相比于大檔案請求時小很多。

  1. 記憶體映射

Linux核心提供一種通路磁盤檔案的特殊方式,它可以将記憶體中某塊位址空間和我們指定的磁盤檔案相關聯,進而對這塊記憶體的通路轉換為對磁盤檔案的通路。這種技術稱為記憶體映射。

多數情況下,記憶體映射可以提高磁盤I/O的性能,無須使用read()或write()等系統調用來通路檔案,而是通過mmap()系統調用來建立記憶體和磁盤檔案的關聯,然後像通路記憶體一樣自由通路檔案。

缺點:在處理較大檔案時,記憶體映射會導緻較大的記憶體開銷,得不償失。

  1. 直接I/O

在linux 2.6中,記憶體映射和直接通路檔案沒有本質差異,因為資料需要經過2次複制,即在磁盤與核心緩沖區之間以及在核心緩沖區與使用者态記憶體空間。

引入核心緩沖區的目的在于提高磁盤檔案的通路性能,然而對于一些複雜的應用,比如資料庫伺服器,它們為了進一步提高性能,希望繞過核心緩沖區,由自己在使用者态空間實作并管理I/O緩沖區,比如資料庫可根據更加合理的政策來提高查詢緩存命中率。另一方面,繞過核心緩沖區也可以減少系統記憶體的開銷,因核心緩沖區本身就在使用系統記憶體。

Linux在open()系統調用中增加參數選項O_DIRECT,即可繞過核心緩沖區直接通路檔案,實作直接I/O。

在Mysql中,對于Innodb存儲引擎,自身進行資料和索引的緩存管理,可在my.cnf配置中配置設定raw分區跳過核心緩沖區,實作直接I/O。

六,改進伺服器并發政策

伺服器并發政策的目的,是讓I/O操作和CPU計算盡量重疊進行,一方面讓CPU在I/O等待時不要空閑,另一方面讓CPU在I/O排程上盡量花最少的時間。

  1. 一個程序處理一個連接配接,非阻塞I/O,使用長連接配接

Apache使用這個模型,其程序的開銷限制了它的并發連接配接數,但從穩定性和相容性的角度,則其相對安全,任何一個子程序的崩潰不會影響Apache本身,Apache父程序可以建立新的子程序;另一方面,Apache經過長期的考驗和廣發的使用,功能子產品非常豐富。是以對于一些并發數要求不高(如150以内),還對其它功能有依賴,那麼可考慮Apache這個模型。

  1. 一個程序處理多個連接配接,異步I/O, 使用長連接配接

一個程序處理多個連接配接,潛在條件就是多路I/O就緒通知的應用。

伺服器通常維護者大量的空閑連接配接,有些可能由于使用長連接配接而在等待逾時,有些可能是網絡傳輸的延時等等,這時epoll隻會關注活躍連接配接,而不在死連接配接上浪費時間,但是select和poll會掃描所有檔案描述符,這個是個非常昂貴的開銷。一個典型的應用就是圖檔伺服器,它們希望為使用者提供網頁中大量圖檔的快速下載下傳,采用長連接配接,但是這些大量連接配接在等待逾時關閉前,處于空閑狀态,這種情況下,epoll依然能很好工作。

POSIX的标準庫(aio.h)中定義了AIO的一系列接口,它幾乎屏蔽了一切網絡通信的細節,對使用者而言非常簡單。AIO沒有提供非阻塞的open()方法,程序仍使用open()系統調用來打開檔案,然後填充一些I/O請求的資料結構(struct aiocb),接下來調用aidread()或aidwrite()來發起異步I/O操作,一旦請求進入操作隊列後,函數便傳回,程序可以在此調用aid_error()來檢查正在運作的I/O操作的狀态

aiocb中相關的域。

七,改進硬體環境

還有一點要提及的是硬體環境,伺服器的硬體配置對站點代理的性能提升肯定是有的,但這裡不作詳細讨論。