在nginx的官網看到一篇介紹nginx原理的文章,這篇文章比較老了是15年發佈的,國內有人翻譯過可是有些小瑕疵,這裏更正出來發布在我我的的文章裏供你們參考,這篇文章詳細的介紹了nginx線程池的原理以及設計思路,在最後經過詳細的實驗數據來告訴咱們經過線程池提高的性能以及分析了應該使用線程池的場景。在往後的其餘領域依然頗有借鑑意義。html
點我看源文linux
你們都知道NGINX使用異步以及事件驅動的方式來處理請求,這意味着,咱們不會爲每一個請求建立另外一個專用的進程或線程(好比像那些使用了傳統架構的服務器)。而是選擇一個工做進程來處理多個鏈接請求。爲了實現這樣的特性,NGINX使用非阻塞模式下的socket以及選擇了更有效率的系統調用好比epoll和kqueue。
滿負載的進程數量不多(一般是每一個cpu核心只佔一個)並且是恆定的,這樣消耗了更少的內存以及cpu時間片沒有被浪費在任務切換上。這個方法的優勢能夠經過nginx這個例子來反映出來。它能夠很是好的併發處理上百萬的請求規模而且處理的效果還不錯。nginx
每一個進程消耗額外的內存,每次進程之間的切換會消耗CPU週期以及產生cpu緩存垃圾數據庫
可是異步,事件驅動這類模型一樣存在問題。或者我更喜歡把這樣的問題稱做敵人。敵人的名字叫作:阻塞。不幸的是,許多第三方模塊都使用了阻塞式的調用,並且用戶(有時候甚至模塊的開發者)沒有意識到這麼作的弊端。阻塞式的操做會毀掉NGINX的性能因此不管如何必定要被阻止。後端
可是在如今的官方版本的NGINX源代碼中也不可能在任何狀況下避免阻塞,爲了解決這個問題新的「線程池」特性被引入到NGINX version 1.7.11以及NGINX PLUS Release 7當中來.它是什麼以及它如何使用這個咱們稍後討論,如今咱們來面對咱們的敵人了。緩存
編輯注-想對NGINX PLUS R7有個大概瞭解能夠在咱們的博客看到更多
想看NGINX PLUS R7其餘特性的具體分析,能夠看下邊列出來的博客:服務器
首先,爲了更好的瞭解NGINX咱們會用幾句話解釋一下它是如何工做的。
大致來講,NGINX是一個事件處理器,一個從內核接收目前的鏈接信息而後發送接下來作的什麼命令給操做系統的控制器。實際上NGINX作的髒活累活是經過協調操做系統來作的,本質上是操做系統在作週期性的讀或者寫。因此對NGINX來講反應的快速及時是很重要的。
工做進程監聽以及處理從內核傳過來的事件
這些事件可能會超時,通知socket讀就緒或者寫就緒,或者通知一個錯誤的產生。NGINX接收到一串事件接着一個一個的處理它們。因此全部的操做能夠在一個線程的一個隊列的一次簡單循環當中完成。NGINX從隊列當中彈出一個事件而後經過好比讀寫socket來作後續處理。在大多數狀況下,這個操做會很快(也許這個操做只須要不多的cpu時間片去從內存當中copy一些數據)而且NGINX能夠用很短的時間在這個隊列當中處理完全部的事件。
全部的操做都在一個線程的簡單循環當中作完了。
可是若是一個長時間而且重量級的操做到來會發生啥呢?答案顯而易見,整個事件處理的循環都會被這個操做所阻塞直到這個操做完成。
所以,所謂「阻塞操做」是指任何致使事件處理循環顯著中止一段時間的操做。操做會由於各類各樣的緣由而被阻塞。好比說,NGINX可能忙於處理冗長的CPU密集型處理,或者可能須要等待訪問資源(例如硬盤驅動器,或一個庫函數以同步方式從數據庫獲取響應,等等等等)。關鍵的問題在於,處理這樣的事情,工做線程不能作其餘別的事情,即便其餘的系統資源能夠獲取到並且隊列當中的其餘一些事件會用到這些資源。
想象一下商店銷售員面前有長長的一長隊人。 隊列中的第一我的須要一個不在商店可是在倉庫裏的東西。 銷售人員跑到倉庫去提貨。 如今,整個隊列必須等待幾個小時才能進行交付,排隊當中的每一個人都不滿意。 想一想若是是你你會如何反應? 隊員中的每一個人的等待時間都增長了這幾個小時,但他們打算買的東西有可能就在店裏。
隊列裏的每一個人都必須由於第一個的訂單而等待。
相同的狀況發生在NGINX當中,想一想當它想要讀一個沒有緩存在內存中的文件而不得不去訪問硬盤的時候。硬盤驅動器很慢(特別是機械硬盤),而等待隊列中的其餘請求可能不須要訪問驅動器,因此它們也是被迫等待的。 所以,延遲增長,系統資源未獲得充分利用。
只有一個阻塞會大幅延遲全部的接下來全部的操做
一些操做系統提供用於讀取和發送文件的異步接口,NGINX可使用此接口(請參閱aio指令)。 這裏有個好例子就是FreeBSD。坑爹的是,Linux可能不如左邊這位那麼友好。 雖然Linux提供了一種用於讀取文件的異步接口,但它有一些顯著的缺點。 其中一個是文件訪問和緩衝區的對齊要求,固然NGINX能夠把這個問題處理得很好。 但第二個問題更糟糕,異步接口須要在文件描述符上設置O_DIRECT標誌,這意味着對文件的任何訪問將繞過內存中的緩存並增長硬盤上的負載。這無形中幹掉了不少本來可使用這個調用的場景。
爲了特別解決這個問題,NGINX 1.7.11和NGINX Plus Release 7中引入了線程池。
如今咱們來看看什麼線程池是關於它們以及它們的工做原理。
讓咱們回到上個問題,倒黴的銷售助理從遙遠的倉庫配貨這個用例。 此次他變得更聰明(也許是被憤怒的客戶羣毆後變得更聰明瞭),他僱傭了送貨服務。 如今,當有人須要遙遠的倉庫裏的一些東西的時候,他不會親自去倉庫而只不過下了一個訂單到送貨服務,他們會處理訂單,而咱們的銷售助理會繼續爲其餘客戶服務。 所以,只有那些貨物不在商店的客戶正在等待交貨,而售貨員能夠立刻繼續爲其餘客戶提供服務。
將訂單傳遞給運送服務從而解除阻塞隊列
在NGINX方面,線程池正在執行運送服務的功能。 它由一個任務隊列和多個處理隊列的線程組成。 當一個工做進程須要作一個潛在的長時間操做時,它不會本身處理這個操做,而是將一個任務放在線程池的隊列中,任何空閒的線程均可以從中進行處理。
工做進程將阻塞操做裝載到線程池
看來咱們還有一個隊列。是的,可是在這種狀況下,隊列受到特定資源的限制。咱們從磁盤讀取資源速度永遠比磁盤生成數據要慢。可是如今至少磁盤操做不會延遲其餘事件的處理,只有須要訪問文件的請求正在等待。
一般將「從磁盤讀取」操做用做阻塞操做的最多見示例,但實際上NGINX中的線程池實現適用於任何不適合在主工做循環中處理的任務。
目前,提交到線程池僅用於三個基本操做:大多數操做系統上的read()系統調用,Linux上的sendfile()和Linux上的在編寫一些臨時文件好比緩存時使用到的aio_write()。咱們將繼續測試和評估,若是有明顯的好處,咱們可能會在將來的版本中將其餘操做也提交到線程池。
編輯注: 在NGINX 1.9.13和NGINX Plus R9中添加了對aio_write()系統調用的支持。
如今到了理論通往實踐的時候了。 爲了演示使用線程池的效果,咱們將執行一個合成基準,模擬阻塞和非阻塞操做的最糟糕組合。
它須要一個確保不適合內存貯存的數據集。 在具備48 GB內存的機器上,咱們已經生成了256 GB的隨機4M分割數據,而後配置了NGINX version 1.9.0來爲其提供服務。
配置很是簡單:
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和accept_mutex被禁用,sendfile被啓用,而且sendfile_max_chunk被設置。 最後一個指令能夠減小阻止sendfile()調用所花費的最大時間,由於NGINX不會一次嘗試發送整個文件,而是分割成512 KB的數據塊來執行相應操做。
該機器有兩塊Intel Xeon E5645(12核24線程)處理器和10 Gbps網絡接口。 磁盤子系統由安裝在RAID10陣列中的四個西數WD1003FBYX硬盤驅動器表示。 操做系統是Ubuntu Server 14.04.1 LTS。
相應基準下負載生成和NGINX的配置。
客戶由兩臺相同的規格的機器組成。其中一個機器上,wrk使用Lua腳本建立負載。腳本以200的併發鏈接從服務器以隨機順序請求文件,和每一個請求均可能會致使緩存缺失從而致使從磁盤讀取產生的阻塞。咱們就叫它「加載隨機載荷」。
第二客戶端機器咱們將運行另外一個副本的wrk,可是這個腳本咱們使用50的併發鏈接來請求相同的文件。由於這個文件被常常訪問的,它將保持在內存中。在正常狀況下,NGINX很快的處理這些請求,可是工做線程若是被其餘的請求阻塞性能將會降低。因此咱們暫且叫它「加載恆定負載」。
性能將由服務器上ifstat監測的吞吐率(throughput)和從第二臺客戶端獲取的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
如你所見,上述的配置能夠產生一共1G的流量,從top命令上咱們能夠看到全部的工做線程在阻塞io上花費了大量的時間(下圖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
在這種狀況下,吞吐率受限於磁盤子系統,而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
請記住,文件是從內存送達的!第一個客戶端的200個鏈接建立的隨機負載,使服務器端的所有的工做進程忙於從磁盤讀取文件,所以產生了過大的延遲,而且沒法在合適的時間內處理咱們的請求。
而後亮出線程池了。爲此,咱們只需在location塊中添加aio threads指令:
location / { root /storage; aio threads; }
接着,執行NGINX reload從新加載配置。
而後,咱們重複上述的測試:
% 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.5 Gbps的流量,對比以前沒有線程池時的1 Gbps高下立判!
理論上還能夠產生更多的流量,可是這已經達到了機器的最大網絡吞吐能力,因此在此次NGINX的測試中,NGINX受限於網絡接口。工做進程的大部分時間只是休眠和等待新的事件(它們在下圖處於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.42秒降到226.32毫秒(減小了33倍),每秒請求處理數提高了31倍(250 vs 8)!
對此,咱們的解釋是請求再也不由於工做進程被阻塞在讀文件而滯留在事件隊列中等待處理,它們能夠被空閒的線程(線程池當中的)處理掉。只要磁盤子系統能撐住第一個客戶端上的隨機負載,NGINX可使用剩餘的CPU資源和網絡容量,從內存中讀取,以服務於上述的第二個客戶端的請求。
在拋出咱們對阻塞操做的擔心並給出一些使人振奮的結果後,可能大部分人已經打算在你的服務器上配置線程池了。可是先彆着急。
實際上很幸運大多數的讀或者寫文件操做都不會和硬盤打交道。若是咱們有足夠的內存來存儲數據集,那麼操做系統會聰明地在被稱做「頁面緩存」的地方緩存那些頻繁使用的文件。
「頁面緩存」的效果很好,可讓NGINX在幾乎全部常見的用例中展現優異的性能。從頁面緩存中讀取比較快,沒有人會說這種操做是「阻塞」。另外一方面,裝載任務到線程池是有必定開銷的。
所以,若是你的機器有合理的大小的內存而且待處理的數據集不是很大的話,那麼無需使用線程池,NGINX已經工做在最優化的方式下。
裝載讀操做到線程池是一種適用於很是特殊任務的技巧。只有當常常請求的內容的大小不適合操做系統的虛擬機緩存時,這種技術纔是最有用的。至於可能適用的場景,好比,基於NGINX的高負載流媒體服務器。這正是咱們已經上文模擬的基準測試的場景。
咱們若是能夠改進裝載讀操做到線程池,將會很是有意義。咱們只須要知道所需的文件數據是否在內存中,只有不在內存中的時候讀操做才應該裝載到線程池的某個單獨的線程中。
再回到售貨員的場景中,這回售貨員不知道要買的商品是否在店裏,他必需要麼老是將全部的訂單提交給運貨服務,要麼老是親自處理它們。
是的,問題的本質就是操做系統沒有這樣的特性。2010年人們第一次試圖把這個功能做爲fincore()系統調用加入到Linux當中,可是沒有成功。後來還有一些是使用RWF_NONBLOCK標記做爲preadv2()系統調用來實現這一功能的嘗試(詳情見LWN.net上的非阻塞緩衝文件讀取操做和異步緩衝讀操做)。但全部這些補丁的命運目前還不明朗。悲催的是,這些補丁尚沒有被內核接受的主要緣由你能夠看這裏:(bikeshedding)。
譯者著:我以爲沒加入內核徹底就是開發組裏面一派人有相似「要啥自行車」這樣的想法.....
另外一方面,FreeBSD的用戶徹底沒必要擔憂。FreeBSD已經具有足夠好的異步讀取文件接口,咱們應該用它而不是線程池。
因此若是你確信在你的用例中使用線程池會帶來好處,那麼如今就是時候深刻了解線程池的配置了。
線程池的配置很是簡單、靈活。首先,獲取NGINX 1.7.11或更高版本的源代碼,使用--with-threads配置參數編譯。在最簡單的場景中,配置也看起來很簡單。全部你須要的全部事就是在合適的狀況下把aio線程的指令include進來:
# in the 'http', 'server', or 'location' context aio threads;
這是線程池的最簡配置。實際上邊的配置是下邊的精簡版:
# in the 'main' context thread_pool default threads=32 max_queue=65536; # in the 'http', 'server', or 'location' context aio threads=default;
這裏定義了一個名爲「default」,包含32個線程,任務隊列最多支持65536個請求的線程池。若是任務隊列過載,NGINX將拒絕請求並輸出以下錯誤日誌:
thread pool "NAME" queue overflow: N tasks waiting
錯誤輸出意味着線程處理做業的速度有可能低於任務入隊的速度了。你能夠嘗試增長隊列的最大值,可是若是這無濟於事,那這意味着你的系統沒有能力處理這麼多的請求了。
正如你已經注意到的,你可使用thread_pool指令,配置線程的數量、隊列的最大長度,以及特定線程池的名稱。最後要說明的是,能夠配置多個相互獨立的線程池,並在配置文件的不一樣位置使用它們來知足不一樣的用途:
# in the 'main' context thread_pool one threads=128 max_queue=0; thread_pool two threads=32; http { server { location /one { aio threads=one; } location /two { aio threads=two; } } #... }
若是沒有指定max_queue參數的值,默認使用的值是65536。如上所示,能夠設置max_queue爲0。在這種狀況下,線程池將使用配置中所有數量的線程來儘量地同時處理多個任務;隊列中不會有等待的任務。
如今,假設咱們有一臺服務器,掛了3塊硬盤,咱們但願把該服務器用做「緩存代理」,緩存後端服務器的所有響應。預期的緩存數據量遠大於可用的內存。它其實是咱們我的CDN的一個緩存節點。毫無疑問,在這種狀況下,最重要的事情是發揮硬盤的最大性能。
咱們的選擇之一是配置一個RAID陣列。這種方法譭譽參半,如今,有了NGINX,咱們能夠有另外的選擇:
# We assume that each of the hard drives is mounted on one of these directories: # /mnt/disk1, /mnt/disk2, or /mnt/disk3 # in the 'main' context thread_pool pool_1 threads=16; thread_pool pool_2 threads=16; thread_pool pool_3 threads=16; http { 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; split_clients $request_uri $disk { 33.3% 1; 33.3% 2; * 3; } server { #... location / { proxy_pass http://backend; proxy_cache_key $request_uri; proxy_cache cache_$disk; aio threads=pool_$disk; sendfile on; } } }
在這份配置中,使用了3個獨立的緩存,每一個緩存專用一塊硬盤,另外,3個獨立的線程池也各自專用一塊硬盤,proxy_cache_path指令在每一個磁盤定義了一個專用、獨立的緩存
split_clients模塊用於高速緩存之間的負載平衡(以及磁盤之間的結果),它徹底適合這類任務。
在 proxy_cache_path指令中設置use_temp_path=off,表示NGINX會將臨時文件保存在緩存數據的同一目錄中。這是爲了不在更新緩存時,磁盤之間互相複製響應數據。
這些調優將發揮磁盤子系統的最優性能,由於NGINX經過單獨的線程池並行且獨立地與每塊磁盤交互。每一個磁盤由16個獨立線程提供支持,而且線程具備用於讀取和發送文件的專用任務隊列。
咱們相信你的客戶會喜歡這種量身定製的方法。請確保你的磁盤撐得住。
這個示例很好地證實了NGINX能夠爲硬件專門調優的靈活性。這就像你給NGINX下了一道命令,要求機器和數據最優配合。並且,經過NGINX在用戶空間中細粒度的調優,咱們能夠確保軟件、操做系統和硬件工做在最優模式下而且儘量有效地利用系統資源。
綜上所述,線程池是個好功能,它將NGINX的性能提升到新的高度而且幹掉了一個衆所周知的長期隱患:阻塞,尤爲是當咱們真正面對大量吞吐的狀況下這種優點更加明顯。
可是還有更多的驚喜。正如前面所述,這種全新的接口可能容許裝載任何耗時和阻塞的操做而不會形成任何性能的損失。 NGINX在大量新模塊和功能方面開闢了新的天地。 許多受歡迎的庫仍然沒有提供異步非阻塞接口,之前這使得它們與NGINX不兼容。 咱們可能花費大量的時間和精力來開發本身的非阻塞原型庫,可是這麼作可能並不值得。 如今,使用線程池,咱們能夠相對容易地使用這些庫,而且這些模塊不會對性能產生影響。
敬請期待下篇文章。