nginx---如何實現輕量級和高並發


 

轉自:https://bijian1013.iteye.com/blog/2232124

Nginx 不同於 Apache2 的一點就是,Nginx 采用單線程,非阻塞,異步 IO 的工作模型。Apache2 對於每一個請求,都會創建一個新進程或線程,會浪費很多內存和 CPU 時間,而 Nginx 使用操作系統提供的IO多路復用技術(epoll), 在一個線程中處理所有的請求。當一個 IO 操作開始的時候,Nginx 不會等待操作完成就會去處理下一個請求,等到某個 IO 操作完成后,Nginx 再回過頭去處理這次 IO 的后續工作。

        Linux系統通過軟限制和硬限制,制約了打開文件的最大個數,而且每個端口偵聽的連接數受限於/etc/sytctl.conf中的ip_local_port_range的范圍,那么nginx是如何做到輕量級和高並發的。

一.Nginx的進程模型


        各個work進程間通過accept_mutex互斥鎖進行連接的獲取,以防止驚群現象的發生(即所有進程都收到通知,卻只有一個進程執行)。負載的實現通過accept_mutex_disable=所有連接數/8-空閑連接數,該值越大越不容易去獲取accept_mutex鎖,也即空閑連接數小於所有連接數的1/8時,不再去主動獲取連接。

二.Nginx處理連接過程

        首先,nginx在啟動時,會解析配置文件,得到需要監聽的端口與ip地址,然后在nginx的master進程里面,先初始化好這個監控的socket(創建socket,設置addrreuse等選項,綁定到指定的ip地址端口,再listen),然后再fork出多個子進程出來,然后子進程會競爭accept新的連接。此時,客戶端就可以向nginx發起連接了。當客戶端與服務端通過三次握手建立好一個連接后,nginx的某一個子進程會accept成功,得到這個建立好的連接的socket,然后創建nginx對連接的封裝,即ngx_connection_t結構體。設置socket的屬性( 比如非阻塞),然后再通過添加讀寫事件,調用connect/read/write來調用連接接着,與客戶端進行數據的交換。最后,nginx或客戶端來主動關掉連接,到此,一個連接就結束了。

三.Nginx的連接與文件描述符的關系

        在nginx中,每個進程會有一個連接數的最大上限,這個上限與系統對fd的限制不一樣。在操作系統中,通過ulimit -n,可以得到一個進程所能夠打開的fd的最大數,即nofile,因為每個socket連接會占用掉一個fd,所以這也會限制進程的最大連接數,當然也會直接影響到程序所能支持的最大並發數,當fd用完后,再創建socket時,就會失敗。nginx通過設置worker_connectons來設置每個進程支持的最大連接數。如果該值大於nofile,那么實際的最大連接數是nofile,nginx會有警告。nginx在實現時,是通過一個連接池來管理的,每個worker進程都有一個獨立的連接池,連接池的大小是worker_connections。這里的連接池里面保存的其實不是真實的連接,它只是一個worker_connections大小的一個ngx_connection_t結構的數組。並且,nginx會通過一個鏈表free_connections來保存所有的空閑ngx_connection_t,每次獲取一個連接時,就從空閑連接鏈表中獲取一個,用完后,再放回空閑連接鏈表里面。

四.結論

        當大量連接進來后,nginx首先把它們放進來,放入得到accept_mutex的work進程的連接池中,此時並沒有真正打開文件描述符,因此可以盡可能多的接受連接請求,真正處理的時候nginx采用異步非阻塞的事件機制,通過單進程循環處理准備好的事件,最終利用有限的系統能打開的最大文件描述來承擔大量的連接請求。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM