五年級英語水平,端午家庭做業。nginx
Nginx以異步、事件驅動的方式處理鏈接。傳統的方式是每一個請求新起一個進程或線程,Nginx沒這樣作,它經過非阻塞sockets、epoll、kqueue等高效手段,實現一個worker進程處理多個鏈接和請求。數據庫
通常狀況下下是一個CPU內核對應一個worker進程,因此worker進程數量固定,而且很少,因此在任務切換上消耗的內存和CPU減小了。這種方式很不錯,在高併發和擴展能力等方面都能體現。緩存
看圖說話,任務切換不見了。服務器
可是異步事件模式很不喜歡阻塞(blocking)。不少第三方模塊使用了阻塞模式的調用,有時候,用戶乃至模塊做者都不知道到阻塞調用會大大下降Nginx的性能。併發
Nginx本身的代碼都有一些場景須要使用到阻塞,因此在1.7.11版本中,引入了新的「線程池」機制,在瞭解這個機制前,咱們先瞅瞅阻塞。app
瞭解阻塞前,先講兩句負載均衡
Nginx其實就是一個事件處理器,接收內核發出的全部與connections相關的事件信息,而後告訴操做系統該作什麼。操做系統如此複雜和底層,因此Nginx的指揮必須叼。ssh
從上圖看,有超時、sockets準備好讀寫、錯誤通知等事件。這些事件都放在一個隊列中,Nginx對事件隊列進行處理。異步
若是一個事件對於到的操做很是耗時,那麼整個隊列的處理就會延遲。socket
「阻塞操做」就是這樣一個致使隊列處理延遲的什麼鬼。舉個例子,CPU密集型計算,資源訪問(硬盤、mutex、同步訪問數據庫等等)。發生阻塞時,worker進程只能等待。
就跟過安檢時同樣,若是你的隊伍裏面有個小朋友帶了一大瓶AD鈣奶,那你只有等他喝完。
有些系統提供的異步文件接口,例如FreeBSD。Linux也提供了相似機制,可是不太好用。首先它要求文件或緩存是扇區對齊的,好吧,Nginx能作到。其次更苛刻的一點是,它要求文件設置O_DIRECT標誌位,這就是說,全部訪問這個文件的操做都是直接讀取,不走任何緩存,這樣反而會增長磁盤IO負擔。
問了解決這些問題,1.7.11版本中引入了線程池。
你家樓下的順豐快遞就是一個線程池,不用每次寄快遞都要去順豐總部,狗屎同樣的比喻。。
對Nginx來講,線程池的做用跟快遞點同樣。它包括一個任務隊列以及配套線程。當一個worker進行須要處理阻塞操做時,它會將這個任務交給線程池來完成。
這裏引入了一個新的隊列,在例子中,這個隊列由於讀取資源致使緩慢,讀取硬盤雖然慢,至少它不會影響事件隊列的繼續處理。
任何阻塞操做均可以放到線程池中。目前,咱們只嘗試了兩個核心操做:主流操做系統的read()系統調用和Linux上的sendfile()。後續通過性能測試會考慮歸入更多的操做。
爲了證明上述理論,進行了以下測試,測試場景包括各類阻塞操做和非阻塞操做。
咱們在一臺48G內存的機器上生成了總共256的隨機文件,每一個文件大小爲4MB。這樣作的目的是保證數據不受內存緩存影響。
簡單的配置以下:
worker_processes 16; events { accept_mutex off; } http { include mime.types; default_type application/octet-stream; access_log off; sendfile on; sendfile_max_chunk 512k; server { listen 8000; location / { root /storage; } } }
配置中進行了一些調優:禁用logging和accpet_mutex,啓用sendfile並設置sendfile_max_chunk,有利於減小阻塞調用sendfile時帶來的總時間。
測試機器配置爲雙Intel至強E5645(共12核-24線程),10G網卡,四塊西數1003FBYX組成的RAID10,系統爲Ubuntu Server 14.04.1 LTS。
兩臺配置同樣的客戶端,一臺機器經過Lua和wrk隨機產生200個併發請求,每一個請求都不會命中緩存,因此Nginx處理時會產生讀盤阻塞操做。另外一臺機器則是產生50個併發請求,每一個請求讀取固定文件,頻繁的文件讀取會命中緩存,因此通常狀況下此類請求處理速度較快,當worker進程阻塞時請求速度會受影響。
經過ifstat和在第二臺機器上wrk來監控系統吞吐性能。
無線程池結果
% ifstat -bi eth2 eth2 Kbps in Kbps out 5531.24 1.03e+06 4855.23 812922.7 5994.66 1.07e+06 5476.27 981529.3 6353.62 1.12e+06 5166.17 892770.3 5522.81 978540.8 6208.10 985466.7 6370.79 1.12e+06 6123.33 1.07e+06
吞吐量大約是1Gbps,從top看,全部的worker進程主要消耗在阻塞I/O上(top中的D狀態)
top - 10:40:47 up 11 days, 1:32, 1 user, load average: 49.61, 45.77 62.89 Tasks: 375 total, 2 running, 373 sleeping, 0 stopped, 0 zombie %Cpu(s): 0.0 us, 0.3 sy, 0.0 ni, 67.7 id, 31.9 wa, 0.0 hi, 0.0 si, 0.0 st KiB Mem: 49453440 total, 49149308 used, 304132 free, 98780 buffers KiB Swap: 10474236 total, 20124 used, 10454112 free, 46903412 cached Mem PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 4639 vbart 20 0 47180 28152 496 D 0.7 0.1 0:00.17 nginx 4632 vbart 20 0 47180 28196 536 D 0.3 0.1 0:00.11 nginx 4633 vbart 20 0 47180 28324 540 D 0.3 0.1 0:00.11 nginx 4635 vbart 20 0 47180 28136 480 D 0.3 0.1 0:00.12 nginx 4636 vbart 20 0 47180 28208 536 D 0.3 0.1 0:00.14 nginx 4637 vbart 20 0 47180 28208 536 D 0.3 0.1 0:00.10 nginx 4638 vbart 20 0 47180 28204 536 D 0.3 0.1 0:00.12 nginx 4640 vbart 20 0 47180 28324 540 D 0.3 0.1 0:00.13 nginx 4641 vbart 20 0 47180 28324 540 D 0.3 0.1 0:00.13 nginx 4642 vbart 20 0 47180 28208 536 D 0.3 0.1 0:00.11 nginx 4643 vbart 20 0 47180 28276 536 D 0.3 0.1 0:00.29 nginx 4644 vbart 20 0 47180 28204 536 D 0.3 0.1 0:00.11 nginx 4645 vbart 20 0 47180 28204 536 D 0.3 0.1 0:00.17 nginx 4646 vbart 20 0 47180 28204 536 D 0.3 0.1 0:00.12 nginx 4647 vbart 20 0 47180 28208 532 D 0.3 0.1 0:00.17 nginx 4631 vbart 20 0 47180 756 252 S 0.0 0.1 0:00.00 nginx 4634 vbart 20 0 47180 28208 536 D 0.0 0.1 0:00.11 nginx 4648 vbart 20 0 25232 1956 1160 R 0.0 0.0 0:00.08 top 25921 vbart 20 0 121956 2232 1056 S 0.0 0.0 0:01.97 sshd 25923 vbart 20 0 40304 4160 2208 S 0.0 0.0 0:00.53 zsh
IO受磁盤限制,CPU多數處於空閒狀態。wrk結果代表性能也較低。
Running 1m test @ http://192.0.2.1:8000/1/1/1 12 threads and 50 connections Thread Stats Avg Stdev Max +/- Stdev Latency 7.42s 5.31s 24.41s 74.73% Req/Sec 0.15 0.36 1.00 84.62% 488 requests in 1.01m, 2.01GB read Requests/sec: 8.08 Transfer/sec: 34.07MB
須要提醒的是,這些請求本來是應該命中緩存很是快速的,可是由於worker進程受第一臺服務器的200併發影響,因此最終比較慢。
接下來對照線程池實驗,在location配置中添加一個aio線程指令
location / { root /storage; aio threads; }
從新加載Nginx配置後,重複上述測試
% ifstat -bi eth2 eth2 Kbps in Kbps out 60915.19 9.51e+06 59978.89 9.51e+06 60122.38 9.51e+06 61179.06 9.51e+06 61798.40 9.51e+06 57072.97 9.50e+06 56072.61 9.51e+06 61279.63 9.51e+06 61243.54 9.51e+06 59632.50 9.50e+06
哇,產生了9.5Gbps的吞吐性能。
性能沒準還能更改,由於已經達到了網卡瓶頸。此次,worker進程主要消耗在sleeping和時間等待上(top中的S狀態)。
top - 10:43:17 up 11 days, 1:35, 1 user, load average: 172.71, 93.84, 77.90 Tasks: 376 total, 1 running, 375 sleeping, 0 stopped, 0 zombie %Cpu(s): 0.2 us, 1.2 sy, 0.0 ni, 34.8 id, 61.5 wa, 0.0 hi, 2.3 si, 0.0 st KiB Mem: 49453440 total, 49096836 used, 356604 free, 97236 buffers KiB Swap: 10474236 total, 22860 used, 10451376 free, 46836580 cached Mem PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 4654 vbart 20 0 309708 28844 596 S 9.0 0.1 0:08.65 nginx 4660 vbart 20 0 309748 28920 596 S 6.6 0.1 0:14.82 nginx 4658 vbart 20 0 309452 28424 520 S 4.3 0.1 0:01.40 nginx 4663 vbart 20 0 309452 28476 572 S 4.3 0.1 0:01.32 nginx 4667 vbart 20 0 309584 28712 588 S 3.7 0.1 0:05.19 nginx 4656 vbart 20 0 309452 28476 572 S 3.3 0.1 0:01.84 nginx 4664 vbart 20 0 309452 28428 524 S 3.3 0.1 0:01.29 nginx 4652 vbart 20 0 309452 28476 572 S 3.0 0.1 0:01.46 nginx 4662 vbart 20 0 309552 28700 596 S 2.7 0.1 0:05.92 nginx 4661 vbart 20 0 309464 28636 596 S 2.3 0.1 0:01.59 nginx 4653 vbart 20 0 309452 28476 572 S 1.7 0.1 0:01.70 nginx 4666 vbart 20 0 309452 28428 524 S 1.3 0.1 0:01.63 nginx 4657 vbart 20 0 309584 28696 592 S 1.0 0.1 0:00.64 nginx 4655 vbart 20 0 30958 28476 572 S 0.7 0.1 0:02.81 nginx 4659 vbart 20 0 309452 28468 564 S 0.3 0.1 0:01.20 nginx 4665 vbart 20 0 309452 28476 572 S 0.3 0.1 0:00.71 nginx 5180 vbart 20 0 25232 1952 1156 R 0.0 0.0 0:00.45 top 4651 vbart 20 0 20032 752 252 S 0.0 0.0 0:00.00 nginx 25921 vbart 20 0 121956 2176 1000 S 0.0 0.0 0:01.98 sshd 25923 vbart 20 0 40304 3840 2208 S 0.0 0.0 0:00.54 zsh
就是說,CPU仍是很富裕。
wrk的結果相差無幾
Running 1m test @ http://192.0.2.1:8000/1/1/1 12 threads and 50 connections Thread Stats Avg Stdev Max +/- Stdev Latency 226.32ms 392.76ms 1.72s 93.48% Req/Sec 20.02 10.84 59.00 65.91% 15045 requests in 1.00m, 58.86GB read Requests/sec: 250.57 Transfer/sec: 0.98GB
4MB文件的請求時間從7.41秒提高至了226.32毫秒(約33倍),QPS提高了大約31倍(250比8)。
提高的緣由再也不贅述,大約就是事件隊列沒有受阻罷了。
看到這裏,是否是立馬就想去修改你的生產環境了,且慢。
事實上,絕大多數的read和sendfile都是在緩存頁中進行的,操做系統會把頻繁使用的文件放在緩存頁中。
當你的數據量較小,而且內存足夠大時,Nginx已是處於最佳狀態了,開線程池反倒會引入開銷。線程池可以良好應對的一個場景,是數據沒法被徹底緩存,例如流媒體服務器,咱們上面的測試環境,就是模擬的流媒體服務。
可否用線程池來提高讀操做的性能呢?惟一須要作的,就是能有效區分哪些文件已經被緩存,哪些文件未緩存。
我們的系統沒有提供這樣的信息。早在2010年Linux嘗試經過fincore()來實現未果。接下來是preadv2()和RWF_NONBLOCK標誌位方式,惋惜也很差用,具體能夠參考內核bikeshedding一文。
哈哈,至少FreeBSD用戶能夠先喝咖啡了,無需在線程池問題上傷腦筋。
若是你確信引入線程池對性能提高有效,那麼我們能夠繼續瞭解一些調優參數。
這些調優都是基於1.7.11+ 版本,編譯選項爲--with-threads參數。最簡單的場景下,僅需在http、server或location區塊配置aio thread參數便可
aio threads;
它對應的完整配置是
thread_pool default threads=32 max_queue=65536; aio threads=default;
默認狀況下包括一個32個線程的線程池,長度爲65536的請求隊列。若是隊列溢出,Nginx會輸出以下錯誤並拒絕請求。
thread pool "NAME" queue overflow: N tasks waiting
這個錯誤表示這個線程池消費小於生產,因此能夠增長隊列長度,若是調整無效,說明系統達到了瓶頸。
另外,咱們能夠調整線程相關的參數,例如對不一樣場景,能夠提供獨立的線程池。
http { thread_pool one threads=128 max_queue=0; thread_pool two threads=32; server { location /one { aio threads=one; } location /two { aio threads=two; } } … }
在未定義max_queue時默認爲65536,當設置成0時,服務能力等同線程數量。
假如你的緩存代理服務器有3塊磁盤,內存不能放下預期須要緩存的文件,因此咱們首先須要讓磁盤工做最大化。
一個方式是RAID,好壞兼併。另外一個方式是Nginx
# We assume that each of the hard drives is mounted on one of the directories: # /mnt/disk1, /mnt/disk2, or /mnt/disk3 accordingly proxy_cache_path /mnt/disk1 levels=1:2 keys_zone=cache_1:256m max_size=1024G use_temp_path=off; proxy_cache_path /mnt/disk2 levels=1:2 keys_zone=cache_2:256m max_size=1024G use_temp_path=off; proxy_cache_path /mnt/disk3 levels=1:2 keys_zone=cache_3:256m max_size=1024G use_temp_path=off; thread_pool pool_1 threads=16; thread_pool pool_2 threads=16; thread_pool pool_3 threads=16; split_clients $request_uri $disk { 33.3% 1; 33.3% 2; * 3; } location / { proxy_pass http://backend; proxy_cache_key $request_uri; proxy_cache cache_$disk; aio threads=pool_$disk; sendfile on; }
使用了3個獨立的緩存,每一個緩存指定到一塊磁盤,而後有3個獨立的線程池。
split_clients模塊用於緩存間的負載均衡。
use_temp_path=off參數讓Nginx將緩存文件保存至文件同級目錄,能夠避免緩存更新時磁盤間的文件數據交換。
明天他媽又要上課了
原文地址:http://nginx.com/blog/thread-pools-boost-performance-9x/