connection
#在Nginx 中connection 是對tcp 連接的封裝,其中包含連接的socket,讀取事件,寫事件。利用 Nginx 封裝的 connection,我們可以很方便的使用 Nginx 來處理與連接相關的事情,例如,建立連接,發送與接受資料等。 ( 推薦學習:nginx使用 )
而Nginx 中的http 請求的處理就是建立在connection之上的,所以Nginx 不只可以作為一個web伺服器,也可以作為郵件伺服器。當然,利用 Nginx 提供的 connection,我們可以與任何後端服務打交道。
結合一個 tcp 連線的生命週期,我們來看看 Nginx 是如何處理一個連線的。
首先,Nginx 在啟動時,會解析設定文件,得到需要監聽的連接埠與ip 位址,然後在Nginx 的master 進程裡面,先初始化好這個監控的socket(創建socket,設定addrreuse 等選項,綁定到指定的ip 位址端口,再listen),然後再fork 出多個子進程出來,然後子進程會競爭accept 新的連接。
此時,客戶端就可以向 Nginx 啟動連線了。
當客戶端與服務端透過三次握手建立好一個連線後,Nginx 的某一個子程序會accept 成功,得到這個建立好的連線的socket,然後建立Nginx 對連線的封裝,也就是ngx_connection_t結構體。
接著,設定讀寫事件處理函數並新增讀寫事件來與客戶端進行資料的交換。最後,Nginx 或客戶端來主動關掉連接,到此,一個連接就壽終正寢了。
當然,Nginx 也是可以作為客戶端來請求其它 server 的資料的(如 upstream 模組),此時,與其它 server 建立的連接,也封裝在 ngx_connection_t 中。
作為客戶端,Nginx 先取得一個 ngx_connection_t 結構體,然後建立 socket,並設定 socket 的屬性( 例如非阻塞)。然後再透過新增讀寫事件,呼叫 connect/read/write 來呼叫連接,最後關掉連接,並釋放 ngx_connection_t。
在 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,每次取得一個連接時,就從空閒連接鍊錶中取得一個,用完後,再放回空閒連結鍊錶裡面。
在這裡,很多人會誤解 worker_connections 這個參數的意思,認為這個值就是 Nginx 所能建立連結的最大值。其實不然,這個值是表示每個 worker 程序所能建立連線的最大值,所以,一個 Nginx 能建立的最大連線數,應該是worker_connections * worker_processes。
當然,這裡說的是最大連線數,對於HTTP 請求本地資源來說,能夠支援的最大並發數量是worker_connections * worker_processes,而如果是HTTP 作為反向代理來說,最大並發數量應該是worker_connections * worker_processes/2。
因為作為反向代理伺服器,每個並發會建立與客戶端的連接和與後端服務的連接,會佔用兩個連接。
那麼,我們前面有說過一個客戶端連接過來後,多個空閒的進程,會競爭這個連接,很容易看到,這種競爭會導致不公平,如果某個進程得到accept的機會比較多,它的空閒連接很快就用完了,如果不提前做一些控制,當accept 到一個新的tcp 連接後,因為無法得到空閒連接,而且無法將此連接轉交給其它進程,最終會導致此tcp 連線無法處理,就中止掉了。
很顯然,這是不公平的,有的進程有剩餘連接,卻沒有處理機會,有的進程因為沒有空餘連接,卻人為地丟棄連接。那麼,如何解決這個問題呢?
首先,Nginx 的處理得先開啟 accept_mutex 選項,此時,只有獲得了 accept_mutex 的進程才會去新增accept事件,也就是說,Nginx會控制進程是否新增 accept 事件。
Nginx 使用一個叫 ngx_accept_disabled 的變數來控制是否去競爭 accept_mutex 鎖定。
在第一段程式碼中,計算ngx_accept_disabled 的值,這個值是Nginx 單一進程的所有連接總數的八分之一,減去剩下的空閒連接數量,得到的這個ngx_accept_disabled 有一個規律,當剩餘連接數小於總連接數的八分之一時,其值才大於0,而且剩餘的連接數越小,這個值越大。
再看第二段程式碼,當ngx_accept_disabled 大於0 時,不會去嘗試取得accept_mutex 鎖,並且將ngx_accept_disabled 減1,於是,每次執行到此處時,都會去減1,直到小於0。
不去取得accept_mutex 鎖,就是等於讓出獲取連線的機會,很顯然可以看出,當空餘連線越少時,ngx_accept_disable 越大,於是讓出的機會就越多,這樣其它進程取得鎖的機會也就越大。
不去 accept,自己的連線就控制下來了,其它進程的連接池就會被利用,這樣,Nginx 就控制了多進程間連接的平衡了。
ngx_accept_disabled = ngx_cycle->connection_n / 8 - ngx_cycle->free_connection_n; if (ngx_accept_disabled > 0) { ngx_accept_disabled--; } else { if (ngx_trylock_accept_mutex(cycle) == NGX_ERROR) { return; } if (ngx_accept_mutex_held) { flags |= NGX_POST_EVENTS; } else { if (timer == NGX_TIMER_INFINITE || timer > ngx_accept_mutex_delay) { timer = ngx_accept_mutex_delay; } } }
連接就先介紹到這,知道在 Nginx 中連接是個什麼東西就行了,而且連接是屬於比較高級的用法。
以上是Nginx的connection是什麼?的詳細內容。更多資訊請關注PHP中文網其他相關文章!