linux - IO阻塞非阻塞、同步异步有什么区别?
PHPz
PHPz 2017-04-17 13:34:25
0
1
807

IO流程是怎样的?nginx、apache是基于哪种IO,为什么?

PHPz
PHPz

学习是最好的投资!

全部回覆(1)
Peter_Zhu

可以用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改造的.

熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板
關於我們 免責聲明 Sitemap
PHP中文網:公益線上PHP培訓,幫助PHP學習者快速成長!