作者:瀟灑一劍
www.cnblogs.com/zengjin93/p/5569556.html
以下內容為入門級介紹,意在對老技術作較全的總結而不是較深的研究,主要參考《構建高性能Web站點》一書,
什么是服務器并發處理能力
一臺服務器在單位時間里能處理的請求越多,服務器的能力越高,也就是服務器并發處理能力越強
有什么方法衡量服務器并發處理能力
1. 吞吐率
吞吐率,單位時間里服務器處理的最大請求數,單位req/s
從服務器角度,實際并發用戶數的可以理解為服務器當前維護的代表不同用戶的檔案描述符總數,也就是并發連接數,
服務器一般會限制同時服務的最多用戶數,比如apache的MaxClents引數,
這里再深入一下,對于服務器來說,服務器希望支持高吞吐率,對于用戶來說,用戶只希望等待最少的時間,顯然,雙方不能滿足,所以雙方利益的平衡點,就是我們希望的最大并發用戶數,
2. 壓力測驗
有一個原理一定要先搞清楚,假如100個用戶同時向服務器分別進行10個請求,與1個用戶向服務器連續進行1000次請求,對服務器的壓力是一樣嗎?
實際上是不一樣的,因對每一個用戶,連續發送請求實際上是指發送一個請求并接收到回應資料后再發送下一個請求,
這樣對于1個用戶向服務器連續進行1000次請求, 任何時刻服務器的網卡接識訓沖區中只有1個請求,而對于100個用戶同時向服務器分別進行10個請求,服務器的網卡接識訓沖區最多有100個等待處理的請求,顯然這時的服務器壓力更大,
壓力測驗前提考慮的條件
-
并發用戶數: 指在某一時刻同時向服務器發送請求的用戶總數(HttpWatch)
-
總請求數
-
請求資源描述
-
請求等待時間(用戶等待時間)
-
用戶平均請求的等待時間
-
服務器平均請求處理的時間
-
硬體環境
壓力測驗中關心的時間又細分以下2種:
-
用戶平均請求等待時間(這里暫不把資料在網路的傳輸時間,還有用戶PC本地的計算時間計算入內)
-
服務器平均請求處理時間
用戶平均請求等待時間主要用于衡量服務器在一定并發用戶數下,單個用戶的服務質量;而服務器平均請求處理時間就是吞吐率的倒數,
一般來說,用戶平均請求等待時間 = 服務器平均請求處理時間 * 并發用戶數
怎么提高服務器的并發處理能力
1. 提高CPU并發計算能力
服務器之所以可以同時處理多個請求,在于作業系統通過多執行流體系設計使得多個任務可以輪流使用系統資源,
這些資源包括CPU,記憶體以及I/O. 這里的I/O主要指磁盤I/O, 和網路I/O,
多行程 & 多執行緒
多執行流的一般實作便是行程,多行程的好處可以對CPU時間的輪流使用,對CPU計算和IO操作重疊利用,這里的IO主要是指磁盤IO和網路IO,相對CPU而言,它們慢的可憐,
而實際上,大多數行程的時間主要消耗在I/O操作上,
現代計算機的DMA技術可以讓CPU不參與I/O操作的全程序,比如行程通過系統呼叫,使得CPU向網卡或者磁盤等I/O設備發出指令,然后行程被掛起,釋放出CPU資源,等待I/O設備完成作業后通過中斷來通知行程重新就緒,
對于單任務而言,CPU大部分時間空閑,這時候多行程的作用尤為重要,CPU 是怎么認識代碼的?推薦大家看下,
多行程不僅能夠提高CPU的并發度,其優越性還體現在獨立的記憶體地址空間和生命周期所帶來的穩定性和健壯性,其中一個行程崩潰不會影響到另一個行程,
但是行程也有如下缺點:
-
fork()系統呼叫開銷很大: prefork
-
行程間調度和背景關系切換成本: 減少行程數量
-
龐大的記憶體重復:共享記憶體
-
IPC編程相對比較麻煩
減少行程切換
當硬體背景關系頻繁裝入和移出時,所消耗的時間是非常可觀的,可用Nmon工具監視服務器每秒的背景關系切換次數,
為了盡量減少背景關系切換次數,最簡單的做法就是減少行程數,盡量使用執行緒并配合其它I/O模型來設計并發策略,
還可以考慮使用行程系結CPU技術,增加CPU快取的命中率,若行程不斷在各CPU上切換,這樣舊的CPU快取就會失效,
減少使用不必要的鎖
服務器處理大量并發請求時,多個請求處理任務時存在一些資源搶占競爭,這時一般采用“鎖”機制來控制資源的占用,到底什么是重入鎖,推薦大家看下,
當一個任務占用資源時,我們鎖住資源,這時其它任務都在等待鎖的釋放,這個現象稱為鎖競爭,
通過鎖競爭的本質,我們要意識到盡量減少并發請求對于共享資源的競爭,
比如在允許情況下關閉服務器訪問日志,這可以大大減少在鎖等待時的延遲時間,要最大程度減少無辜的等待時間,
這里說下無鎖編程,就是由內核完成這個鎖機制,主要是使用原子操作替代鎖來實作對共享資源的訪問保護,
使用原子操作時,在進行實際的寫操作時,使用了lock指令,這樣就可以阻止其他任務寫這塊記憶體,避免出現資料競爭現象,原子操作速度比鎖快,一般要快一倍以上,
例如fwrite(), fopen(),其是使用append方式寫檔案,其原理就是使用了無鎖編程,無鎖編程的復雜度高,但是效率快,而且發生死鎖概率低,
關注微信公眾號:Java技術堆疊,在后臺回復:多執行緒,可以獲取我整理的 N 篇最新 Java多執行緒教程,都是干貨,
考慮行程優先級
行程調度器會動態調整運行佇列中行程的優先級,通過top觀察行程的PR值
考慮系統負載
可在任何時刻查看/proc/loadavg, top中的load average也可看出
考慮CPU使用率
除了用戶空間和內核空間的CPU使用率以外,還要關注I/O wait,它是指CPU空閑并且等待I/O操作完成的時間比例(top中查看wa的值),
2. 考慮減少記憶體分配和釋放
服務器的作業程序中,需要大量的記憶體,使得記憶體的分配和釋放作業尤為重要,
可以通過改善資料結構和演算法復制度來適當減少中間臨時變數的記憶體分配及資料復制時間,而服務器本身也使用了各自的策略來提高效率,
例如Apache,在運行開始時一次申請大片的記憶體作為記憶體池,若隨后需要時就在記憶體池中直接獲取,不需要再次分配,避免了頻繁的記憶體分配和釋放引起的記憶體整理時間,
再如Nginx使用多執行緒來處理請求,使得多個執行緒之間可以共享記憶體資源,從而令它的記憶體總體使用量大大減少,
另外,Nginx分階段的記憶體分配策略,按需分配,及時釋放,使得記憶體使用量保持在很小的數量范圍,
另外,還可以考慮共享記憶體,
共享記憶體指在多處理器的計算機系統中,可以被不同中央處理器(CPU)訪問的大容量記憶體,也可以由不同行程共享,是非常快的行程通信方式,
但是使用共享記憶體也有不好的地方,就是對于多機器時資料不好統一,
shell命令ipcs可用來顯示系統下共享記憶體的狀態,函式shmget可以創建或打開一塊共享記憶體區,函式shmat將一個存在的共享記憶體段連接到本行程空間, 函式shmctl可以對共享記憶體段進行多種操作,函式shmdt函式分離該共享記憶體,
3. 考慮使用持久連接
持久連接也為長連接,它本身是TCP通信的一種普通方式,即在一次TCP連接中持續發送多分資料而不斷開連接,
與它相反的方式稱為短連接,也就是建立連接后發送一份資料就斷開,然后再次建立連接發送下一份資料, 周而復始,
是否采用持久連接,完全取決于應用特點,
從性能角度看,建立TCP連接的操作本身是一項不小的開銷,在允許的情況下,連接次數越少,越有利于性能的提升; 尤其對于密集型的圖片或網頁等小資料請求處理有明顯的加速所用,
HTTP長連接需要瀏覽器和web服務器的共同協作,目前瀏覽器普遍支持長連接,表現在其發出的HTTP請求資料頭中包含關于長連接的宣告,如下:Connection: Keep-Alive
主流的web服務器都支持長連接,比如apache中,可以用KeepAlive off關閉長連接,
對于長連接的有效使用,還有關鍵一點在于長連接超時時間的設定,即長連接在什么時候關閉嗎?
Apache的默認設定為5s, 若這個時間設定過長,則可能導致資源無效占有,維持大量空閑行程,影響服務器性能,
4. 改進I/O 模型
I/O操作根據設備的不同分為很多型別,比如記憶體I/O, 網路I/O, 磁盤I/O,詳解 Java 中 4 種 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的參與方式,
DMA技術
I/O設備和記憶體之間的資料傳輸方式由DMA控制器完成,在DMA模式下,CPU只需向DMA下達命令,讓DMA控制器來處理資料的傳送,這樣可以大大節省系統資源,
異步I/O
異步I/O指主動請求資料后便可以繼續處理其它任務,隨后等待I/O操作的通知,這樣行程在資料讀寫時不發生阻塞,
異步I/O是非阻塞的,當函式回傳時,真正的I/O傳輸已經完成,這讓CPU處理和I/O操作達到很好的重疊,
I/O多路復用
epoll服務器同時處理大量的檔案描述符是必不可少的,若采用同步非阻塞I/O模型,若同時接收TCP連接的資料,就必須輪流對每個socket呼叫接收資料的方法,不管這些socket有沒有可接收的資料,都要詢問一次,
假如大部分socket并沒有資料可以接收,那么行程便會浪費很多CPU時間用于檢查這些socket有沒有可以接收的資料,
多路I/O就緒通知的出現,提供了對大量檔案描述符就緒檢查的高性能方案,它允許行程通過一種方法同時監視所有檔案描述符,并可以快速獲得所有就緒的檔案描述符,然后只針對這些檔案描述符進行資料訪問,
epoll可以同時支持水平觸發和邊緣觸發,理論上邊緣觸發性能更高,但是代碼實作復雜,因為任何意外的丟失事件都會造成請求處理錯誤,
epoll主要有2大改進:
-
epoll只告知就緒的檔案描述符,而且當呼叫epoll_wait()獲得檔案描述符時,回傳并不是實際的描述符,而是一個代表就緒描述符數量的值,然后只需去epoll指定的一個陣列中依次取得相應數量的檔案描述符即可,
這里使用了記憶體映射(mmap)技術,這樣徹底省掉了這些檔案描述符在系統呼叫時復制的開銷,
-
epoll采用基于事件的就緒通知方式,其事先通過epoll_ctrl()注冊每一個檔案描述符,一旦某個檔案描述符就緒時,內核會采用類似callback的回呼機制,當行程呼叫epoll_wait()時得到通知
關于IO模型,可以參考筆者前面寫的相關文章Java NIO.2;關于epoll,可以參考筆者前面寫的文章select、poll和epoll簡介,
Sendfile
大多數時候,我們都向服務器請求靜態檔案,比如圖片,樣式表等,
在處理這些請求時,磁盤檔案的資料先經過內核緩沖區,然后到用戶記憶體空間,不需經過任何處理,其又被送到網卡對應的內核緩沖區,接著再被送入網卡進行發送,
Linux提供sendfile()系統呼叫,可以講磁盤檔案的特定部分直接傳送到代表客戶端的socket描述符,加快了靜態檔案的請求速度,同時減少CPU和記憶體的開銷,
適用場景:對于請求較小的靜態檔案,sendfile發揮的作用不那么明顯,因發送資料的環節在整個程序中所占時間的比例相比于大檔案請求時小很多,
記憶體映射
Linux內核提供一種訪問磁盤檔案的特殊方式,它可以將記憶體中某塊地址空間和我們指定的磁盤檔案相關聯,從而對這塊記憶體的訪問轉換為對磁盤檔案的訪問,這種技術稱為記憶體映射,
多數情況下,記憶體映射可以提高磁盤I/O的性能,無須使用read()或write()等系統呼叫來訪問檔案,而是通過mmap()系統呼叫來建立記憶體和磁盤檔案的關聯,然后像訪問記憶體一樣自由訪問檔案,
缺點:在處理較大檔案時,記憶體映射會導致較大的記憶體開銷,得不償失,
直接I/O
在linux 2.6中,記憶體映射和直接訪問檔案沒有本質差異,因為資料需要經過2次復制,即在磁盤與內核緩沖區之間以及在內核緩沖區與用戶態記憶體空間,
引入內核緩沖區的目的在于提高磁盤檔案的訪問性能,然而對于一些復雜的應用,比如資料庫服務器,它們為了進一步提高性能,希望繞過內核緩沖區,由自己在用戶態空間實作并管理I/O緩沖區,比如資料庫可根據更加合理的策略來提高查詢快取命中率,
另一方面,繞過內核緩沖區也可以減少系統記憶體的開銷,因內核緩沖區本身就在使用系統記憶體,
Linux在open()系統呼叫中增加引數選項O_DIRECT,即可繞過內核緩沖區直接訪問檔案,實作直接I/O,
在Mysql中,對于Innodb存盤引擎,自身進行資料和索引的快取管理,可在my.cnf配置中分配raw磁區跳過內核緩沖區,實作直接I/O,
5. 改進服務器并發策略
服務器并發策略的目的,是讓I/O操作和CPU計算盡量重疊進行,一方面讓CPU在I/O等待時不要空閑,另一方面讓CPU在I/O調度上盡量花最少的時間,
一個行程處理一個連接,非阻塞I/O
這樣會存在多個并發請求同時到達時,服務器必然要準備多個行程來處理請求,其行程的開銷限制了它的并發連接數,
但從穩定性和兼容性的角度,則其相對安全,任何一個子行程的崩潰不會影響服務器本身,父行程可以創建新的子行程;這種策略典型的例子就是Apache的fork和prefork模式,
對于并發數不高(如150以內)的站點同時依賴Apache其它功能時的應用選擇Apache還是可以的,
一個執行緒處理一個連接,非阻塞IO
這種方式允許在一個行程中通過多個執行緒來處理多個連接,一個執行緒處理一個連接,Apache的worker模式就是這種典型例子,使其可支持更多的并發連接,不過這種模式的總體性能還不如prefork,所以一般不選用worker模式,推薦閱讀:14個Java并發容器,
一個行程處理多個連接,異步I/O
一個執行緒同時處理多個連接,潛在的前提條件就是使用IO多路復用就緒通知,
這種情況下,將處理多個連接的行程叫做worker行程或服務行程,worker的數量可以配置,如Nginx中的worker_processes 4,
一個執行緒處理多個連接,異步IO
即使有高性能的IO多路復用就緒通知,但磁盤IO的等待還是無法避免的,更加高效的方法是對磁盤檔案使用異步IO,目前很少有Web服務器真正意義上支持這種異步IO,
6. 改進硬體環境
還有一點要提及的是硬體環境,服務器的硬體配置對應用程式的性能提升往往是最直接,也是最簡單的方式,這就是所謂的scale up,這里不做論述,
關注公眾號Java技術堆疊回復"面試"獲取我整理的2020最全面試題及答案,
推薦去我的博客閱讀更多:
1.Java JVM、集合、多執行緒、新特性系列教程
2.Spring MVC、Spring Boot、Spring Cloud 系列教程
3.Maven、Git、Eclipse、Intellij IDEA 系列工具教程
4.Java、后端、架構、阿里巴巴等大廠最新面試題
覺得不錯,別忘了點贊+轉發哦!
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/142341.html
標籤:Java
上一篇:JSON的介紹
