首頁  >  問答  >  主體

Nginx內部有使用多執行緒嗎?

Nginx把一個request的處理分為多個階段(phases)。這些階段會有IO阻塞嗎?如果有了阻塞,Nginx會去執行其他requests,但是執行其他requests的時候會有優先權區分嗎(會先執行已經進行到後面階段的requests嗎?)?還有,Nginx會在每個階段都有一個thread pool來處理,還是至始至終就他自己一個thread?

某草草某草草2713 天前646

全部回覆(1)我來回復

  • 伊谢尔伦

    伊谢尔伦2017-05-16 17:23:17

    cat /proc/3776/status|grep Threads 可見Nginx工作進程只有1個線程,其中3776是Nginx工作進程的PID。另外Nginx從1.7.11加入了AIO線程池支持,能夠使用AIO多線程讀取和發送大文件,以免工人進程被阻塞(小文件用sendfile,大文件用AIO線程池),要啟用線程池支持, configure時需要明確加入--with-threads選項。
    https://www.nginx.com/blog/thread-pools-boost-performance-9x/
    http://nginx.org/en/docs/ngx_core_module.html#thread_pool

    轉:
    當listen_fd有新的accept()請求過來,操作系統會喚醒所有子進程,因為這些進程都epoll_wait()同一個listen_fd,操作系統又無從判斷由誰來負責accept,索性乾脆全部叫醒,但最終只會有一個進程成功accept,其他進程accept失敗.所有子進程都是被"嚇醒"的,所以稱之為Thundering Herd(驚群).

    監聽套接字在啟動時就完成初始化,worker進程通過這些套接字接受,讀取請求和輸出響應.Nginx並沒有像PHP-FPM那樣採用master進程來分發請求,這個工作由操作系統內核機制完成,所以可能會導致驚群現象,也就是當listen_fd有新的accept()請求過來,作業系統會喚醒所有子進程.

    Nginx解決驚群的思路:避免驚群.
    http://nginx.org/en/docs/ngx_core_module.html#accept_mutex
    具體措施有使用全域互斥鎖(accept_mutex on),每個工作進程在epoll_wait ()之前先去申請鎖,申請到則繼續處理,獲取不到則等待,並設定了一個負載平衡的演算法(當某一個工作進程的任務量達到總設定量的7/8時,則不會再嘗試去申請鎖)來均衡各個進程的任務量.

    Nginx解決驚群的新方法:使用核心提供的Socket ReusePort功能
    NGINX 1.9.1 支援socket分片:
    http://nglua.com/docs/sharding.html
    NGINX1.9.1支援socket的SO_REUSEPORTet,這個選項在許多作業系統的新版本有效,包括DragonFly BSD和Linux(3.9+核心).這個選項允許多個socket監聽同一個IP位址和連接埠的組合.核心負載均衡這些進來的sockets連接,將這些socket有效的分片.當SO_REUSEPORT選項沒開啟時,連接進來時監聽socket默認會通知某個進程.如果accept_mutex off這個指令,此時會喚醒所有的工作進程,它們將為了得到它產生競爭,這就是所謂的驚群現象.如果使用epoll且不用鎖(accept_mutex off),當監聽端口有讀操作時,是會產生驚群現象的.啟用SO_REUSEPORT選項後,每個進程都有自己獨立的監聽socket.內核決定哪個是有效的socket(進程)得到這個連接.這樣做降低了延遲並提高了工作進程的性能,它也意味著工作進程在準備處理它們之前被賦予了新的連接.

    nginx預設以多進程的方式工作,一個master進程和多個worker進程,master進程主要用來管理worker進程.多個worker進程同等競爭來自客戶端的請求,一個worker進程可以處理多個請求,但不能處理其它worker進程的請求.每個worker進程裡面只有一個主線程,在epoll支持下,採用異步非阻塞的方式來處理請求,從而實現高並發.epoll支持監聽多個事件(socket輪詢),當事件沒準備好時,放到epoll裡面,事件準備好了,就去讀寫.與多線程相比,這種事件處理方式是有很大的優勢的,不需要創建線程,每個請求佔用的記憶體也很少,沒有上下文切換,事件處理非常的輕量級.並發數再多也不會導致無謂的資源浪費(上下文切換),更多的並發數,只是會佔用更多的內存而已.而httpd常用的工作方式是每個請求會獨佔一個工作線程,當並發數上到幾千時,就同時有幾千的線程在處理請求了,這對操作系統來說,是個不小的挑戰.線程帶來的記憶體佔用非常大,線程的上下文切換帶來的cpu開銷很大,httpd的性能自然就上不去了.Tengine團隊之前有對連接數進行過測試,在24G內存的機器上,Nginx處理的並發請求數達到過200萬.(平均1G記憶體可以處理8萬多請求)Nginx支援將某一個進程綁定在某一個核上(CPU親緣性綁定),這樣就不會因為進程的切換帶來cache的失效,所以推薦設定cpu有幾個核就設定幾個worker進程.但注意,過多的worker進程,只會導致進程來競爭cpu資源了,從而帶來不必要的上下文切換,所以worker進程不是越多越好.詳細參考:
    http://tengine.taobao.org/book/chapter_02.html

    回覆
    0
  • 取消回覆