84669 人學習
152542 人學習
20005 人學習
5487 人學習
7821 人學習
359900 人學習
3350 人學習
180660 人學習
48569 人學習
18603 人學習
40936 人學習
1549 人學習
1183 人學習
32909 人學習
IO流程是怎样的?nginx、apache是基于哪种IO,为什么?
学习是最好的投资!
可以用sudo strace -p PID 查看編號的PID的進程的系統調用.nginx master: rt_sigsuspendnginx worker: epoll_waitphp-fpm master: epoll_wait (events.mechanism = epoll) php-fpm worker: accept poll可見Nginx工作進程進行了epoll_wait系統呼叫,epoll是Linux核心提供的非同步網路IO程式介面.
http://liuxun.org/blog/nginx-gong-zuo-jin-cheng-mo-xing/Nginx並沒有像PHP-FPM那樣採用master進程來分發連接,這個工作由操作系統核心機製完成,所以可能會導致驚群現象,也就是當listen_fd有新的accept()請求過來,作業系統會喚醒所有子程序.Nginx透過全域互斥鎖來避免驚群( accept_mutex on),每個工作進程在epoll_wait()之前先去申請鎖,申請到則繼續處理,獲取不到則等待,並設定了一個負載均衡的演算法(當某一個工作進程的任務量達到總設定量的7/8時,則不會再嘗試去申請鎖)來均衡各個進程的任務量.http://nginx.org/en/docs/ngx_core_module.html#accept_mutex
Nginx解決驚群的新方法:使用核心提供的Socket ReusePort功能NGINX 1.9.1 支援socket分片:http://nglua.com/docs/sharding.htmlhttp ://nginx.com/blog/socket-sharding-nginx-release-1-9-1/NGINX1.9.1支援socket的SO_REUSEPORT選項,這個選項在許多作業系統的新版本有效,包括DragonFly BSD和Linux(3.9+核心).這個選項允許多個socket監聽同一個IP位址和連接埠的組合.核心負載平衡這些進來的sockets連接,將這些socket有效的分片.當SO_REUSEPORT選項沒開啟時,連接進來時監聽socket默認會通知某個進程.如果accept_mutex off這個指令,此時會喚醒所有的工作進程,它們將為了得到它產生競爭,這就是所謂的驚群現象.如果使用epoll且不用鎖(accept_mutex off),當監聽埠有讀取操作時,是會產生驚群現象的.啟用SO_REUSEPORT選項後,每個行程將有個獨立的監聽socket.內核決定哪個是有效的socket(進程)得到這個連接.這樣做降低了延遲並提高了工作進程的性能,它也意味著工作進程在準備處理它們之前被賦予了新的連接.開啟SO_REUSEPORT支援,只需將新的參數reuseport加到listen指令的後面:listen 80 reuseport;包含這個reuseport參數後將禁用這個監聽socket的accept_mutex,因為鎖變得多餘了.基準測試:NGINX 1.9.1啟用reuseport完美解決驚群後,每秒處理的請求數提升了2到3倍,同時降低了延遲和stdev指標.附:淘寶的Tengine據說很早就加入了socket分片功能.
上面說的是網路IO的非同步,下面說磁碟IO的異步.
在高效能的伺服器程式設計中,I/O模型理所當然的是重中之重,需要謹慎選型.對於網路套接字,我們可以採用epoll的方式來輪詢,儘管epoll也有一些缺陷,但總體來說還是很高效的,尤其來大量套接字的場景下.但對於Regular File來說,是不能夠用的.採用poll/epoll,即O_NOBLOCK方式對於傳統文件句柄是無效的.也就是說我們的open,read,mkdir之類的Regular File操作必定會導致阻塞.在多線程,多進程模型中,可以選擇以同步阻塞的方式來進行IO操作,任務調度由作業系統來保證公平性.
NGINX從1.7.11試驗性引入線程池,特定場景效能提升9倍.https://www.nginx.com/blog/thread-pools-boost-performance-9x/測試伺服器有2個Intel Xeon E5645處理器(共:12核心,24超線程)和10-Gbps的網路介面.磁碟子系統是由4塊西部資料WD1003FBYX磁碟組成的RAID10陣列.操作系統是Ubuntu Server 14.04.1 LTS.
thread_pool default threads=32 max_queue=65536;aio threads=default;這裡定義了一個名為default,包含32個執行緒,任務佇列最多支援65536個請求的執行緒池.如果任務佇列過載,NGINX將輸出以下錯誤日誌並拒絕請求:thread pool "NAME" queue overflow: N tasks waiting
有了這個線程池,NGINX有可能沒有任何性能損失地卸載任何長期阻塞的操作.許多流行的庫仍然沒有提供異步非阻塞接口,此前,這使得它們無法與NGINX兼容.我們可以花大量的時間和資源,去開發我們自己的無阻塞原型庫,但這麼做始終都是值得的嗎?現在,有了線程池,我們可以相對容易地使用這些庫,而不會影響這些模組的性能.FreeBSD已經有足夠好的異步接口來讀取文件,這時候不需要使用線程池.Linux缺乏這樣的機制,所以對於一些不適合緩存在虛擬記憶體的檔案(大檔案),可以卸載讀取操作到AIO線程池,避免阻塞工作進程.我們如果可以改進卸載讀取操作到線程池,將會非常有意義.我們只需要知道所需的檔案資料是否在記憶體中,只有不在記憶體中時,讀操作才應該卸載到一個單獨的執行緒中.
Linux核心提供的非同步檔案操作介面AIO,需要DirectIO,無法利用記憶體的Page Cache,這種奇怪的實作可能是Oracle/IBM專門為資料庫應用設計的,MySQL就用到了AIO.AIO+DirectIO繞過了虛擬檔案系統VFS高速緩存固然讓大型資料庫系統如Oracle,MySQL(InnoDB)非常高興,因為Oracle,MySQL都有自己的快取系統,所以不需要作業系統的Page Cache快取,DirectIO能避免資料就被快取兩次而浪費記憶體.也就是說,如果你想讓自己的程式透過AIO實現非同步檔案IO,那麼你最好建立自己的記憶體快取系統,而不是依賴內核.
最後,Linux上Apache 2.4系列預設的event MPM,是一個多進程,每個工作進程包含多個執行緒的epoll事件驅動的MPM.Apache 2.2系列的prefork MPM是純粹的多進程架構,沒有引入多執行緒,也沒有使用核心的epoll特性,估計主要是為了方便移植.IBM AIX上面的IBM HTTP Server貌似就是基於Apache改造的.
可以用sudo strace -p PID 查看編號的PID的進程的系統調用.
nginx master: rt_sigsuspend
nginx worker: epoll_wait
php-fpm master: epoll_wait (events.mechanism = epoll)
php-fpm worker: accept poll
可見Nginx工作進程進行了epoll_wait系統呼叫,epoll是Linux核心提供的非同步網路IO程式介面.
http://liuxun.org/blog/nginx-gong-zuo-jin-cheng-mo-xing/
Nginx並沒有像PHP-FPM那樣採用master進程來分發連接,這個工作由操作系統核心機製完成,
所以可能會導致驚群現象,也就是當listen_fd有新的accept()請求過來,作業系統會喚醒所有子程序.
Nginx透過全域互斥鎖來避免驚群( accept_mutex on),每個工作進程在epoll_wait()之前先去申請鎖,申請到則繼續處理,獲取不到則等待,
並設定了一個負載均衡的演算法(當某一個工作進程的任務量達到總設定量的7/8時,則不會再嘗試去申請鎖)來均衡各個進程的任務量.
http://nginx.org/en/docs/ngx_core_module.html#accept_mutex
Nginx解決驚群的新方法:使用核心提供的Socket ReusePort功能
NGINX 1.9.1 支援socket分片:
http://nglua.com/docs/sharding.html
http ://nginx.com/blog/socket-sharding-nginx-release-1-9-1/
NGINX1.9.1支援socket的SO_REUSEPORT選項,這個選項在許多作業系統的新版本有效,包括DragonFly BSD和Linux(3.9+核心).
這個選項允許多個socket監聽同一個IP位址和連接埠的組合.核心負載平衡這些進來的sockets連接,將這些socket有效的分片.
當SO_REUSEPORT選項沒開啟時,連接進來時監聽socket默認會通知某個進程.
如果accept_mutex off這個指令,此時會喚醒所有的工作進程,它們將為了得到它產生競爭,這就是所謂的驚群現象.
如果使用epoll且不用鎖(accept_mutex off),當監聽埠有讀取操作時,是會產生驚群現象的.
啟用SO_REUSEPORT選項後,每個行程將有個獨立的監聽socket.內核決定哪個是有效的socket(進程)得到這個連接.
這樣做降低了延遲並提高了工作進程的性能,它也意味著工作進程在準備處理它們之前被賦予了新的連接.
開啟SO_REUSEPORT支援,只需將新的參數reuseport加到listen指令的後面:
listen 80 reuseport;
包含這個reuseport參數後將禁用這個監聽socket的accept_mutex,因為鎖變得多餘了.
基準測試:NGINX 1.9.1啟用reuseport完美解決驚群後,每秒處理的請求數提升了2到3倍,同時降低了延遲和stdev指標.
附:淘寶的Tengine據說很早就加入了socket分片功能.
上面說的是網路IO的非同步,下面說磁碟IO的異步.
在高效能的伺服器程式設計中,I/O模型理所當然的是重中之重,需要謹慎選型.
對於網路套接字,我們可以採用epoll的方式來輪詢,儘管epoll也有一些缺陷,但總體來說還是很高效的,尤其來大量套接字的場景下.
但對於Regular File來說,是不能夠用的.採用poll/epoll,即O_NOBLOCK方式對於傳統文件句柄是無效的.
也就是說我們的open,read,mkdir之類的Regular File操作必定會導致阻塞.
在多線程,多進程模型中,可以選擇以同步阻塞的方式來進行IO操作,任務調度由作業系統來保證公平性.
NGINX從1.7.11試驗性引入線程池,特定場景效能提升9倍.
https://www.nginx.com/blog/thread-pools-boost-performance-9x/
測試伺服器有2個Intel Xeon E5645處理器(共:12核心,24超線程)和10-Gbps的網路介面.
磁碟子系統是由4塊西部資料WD1003FBYX磁碟組成的RAID10陣列.
操作系統是Ubuntu Server 14.04.1 LTS.
thread_pool default threads=32 max_queue=65536;
aio threads=default;
這裡定義了一個名為default,包含32個執行緒,任務佇列最多支援65536個請求的執行緒池.
如果任務佇列過載,NGINX將輸出以下錯誤日誌並拒絕請求:
thread pool "NAME" queue overflow: N tasks waiting
有了這個線程池,NGINX有可能沒有任何性能損失地卸載任何長期阻塞的操作.
許多流行的庫仍然沒有提供異步非阻塞接口,此前,這使得它們無法與NGINX兼容.
我們可以花大量的時間和資源,去開發我們自己的無阻塞原型庫,但這麼做始終都是值得的嗎?
現在,有了線程池,我們可以相對容易地使用這些庫,而不會影響這些模組的性能.
FreeBSD已經有足夠好的異步接口來讀取文件,這時候不需要使用線程池.
Linux缺乏這樣的機制,所以對於一些不適合緩存在虛擬記憶體的檔案(大檔案),可以卸載讀取操作到AIO線程池,避免阻塞工作進程.
我們如果可以改進卸載讀取操作到線程池,將會非常有意義.
我們只需要知道所需的檔案資料是否在記憶體中,只有不在記憶體中時,讀操作才應該卸載到一個單獨的執行緒中.
Linux核心提供的非同步檔案操作介面AIO,需要DirectIO,無法利用記憶體的Page Cache,
這種奇怪的實作可能是Oracle/IBM專門為資料庫應用設計的,MySQL就用到了AIO.
AIO+DirectIO繞過了虛擬檔案系統VFS高速緩存固然讓大型資料庫系統如Oracle,MySQL(InnoDB)非常高興,
因為Oracle,MySQL都有自己的快取系統,所以不需要作業系統的Page Cache快取,DirectIO能避免資料就被快取兩次而浪費記憶體.
也就是說,如果你想讓自己的程式透過AIO實現非同步檔案IO,那麼你最好建立自己的記憶體快取系統,而不是依賴內核.
最後,Linux上Apache 2.4系列預設的event MPM,是一個多進程,每個工作進程包含多個執行緒的epoll事件驅動的MPM.
Apache 2.2系列的prefork MPM是純粹的多進程架構,沒有引入多執行緒,也沒有使用核心的epoll特性,估計主要是為了方便移植.
IBM AIX上面的IBM HTTP Server貌似就是基於Apache改造的.