2011-03-17 76 views
4

the Nginx documentationNginx如何在不丟失任何請求的情況下進行升級?

如果你需要用一個新的來代替Nginx的二進制 (升級到 新版本時,或添加/刪除服務器 模塊),你可以做到這一點沒有任何 服務中斷 - 沒有傳入的 請求將會丟失。

我的同事和我試圖弄清楚:這是如何工作的?。我們知道(我們認爲):

  • 只有一個進程可以在80端口在
  • Nginx的創建一個套接字並將其連接到端口80
  • 父進程及其任意子的時間傾聽都可以綁定到同一個插座,這是nginx怎麼可以有多個工子女響應請求

我們也做了一些實驗nginx的,就像這樣:

  • 發送kill -USR2當前的主進程
  • 反覆運行ps -ef | grep unicorn看到任何麒麟過程中,用自己的PID和他們的父母的PID
  • 注意的是,新的主進程是,首先,老主人的孩子進程,但是當舊的主進程結束時,新的主進程有ppi

顯然,新的主進程可以在它們都運行時監聽與舊進程相同的套接字,因爲那時候,新的主人是老主人的孩子。但不知何故,新的主流程可以成爲......呃......沒有人的孩子?

我認爲這是標準的Unix的東西,但我對進程和端口和套接字的理解是相當模糊的。任何人都可以更詳細地解釋這一點嗎?我們的任何假設都錯了嗎?有沒有一本我可以閱讀的書來真正理解這些東西?

+1

應該注意的是,根據nginx文檔,舊的和新的主進程都處理連接,直到您發送一個WINCH信號給舊的主進程。 – Adam 2011-03-17 21:40:28

回答

4

具體說明:http://www.csc.villanova.edu/~mdamian/Sockets/TcpSockets.htm描述了用於TCP套接字的C庫。

我認爲關鍵是在一個進程分支持有套接字文件描述符後,父和子都能夠對它調用accept()。

所以這裏是流程。 Nginx,正常啓動:

  1. 調用socket()和bind()和listen()來設置一個套接字,由文件描述符(整數)引用。
  2. 啓動一個在循環中調用文件描述符上的accept()以處理傳入連接的線程。

然後Nginx叉。父母照常運行,但孩子立即執行新的二進制文件。 exec()清除舊程序,內存和正在運行的線程,但繼承了打開的文件描述符:請參閱http://linux.die.net/man/2/execve。我懷疑exec()調用將打開的文件描述符的編號作爲命令行參數傳遞。

孩子,開始作爲升級的一部分:

  1. 讀取命令行打開的文件描述符的數量。
  2. 啓動一個在循環中調用文件描述符上的accept()以處理傳入連接的線程。
  3. 告訴父母流失(停止接受()ing,並完成現有的連接),並死亡。
1

我不知道nginx是怎麼做的,但基本上,它可能只是exec新的二進制文件,攜帶它的監聽套接字的新進程(實際上,它仍然是相同的進程,它只是取代了在其中執行的程序)。監聽套接字有傳入連接的積壓,只要啓動速度足夠快,它應該能夠在溢出之前開始處理它們。如果不是的話,它可能會先分叉exec,然後等待它啓動,直到它準備好處理傳入請求,然後交出偵聽套接字的命令(文件描述符在分支時被繼承,都可以訪問它)通過一些內部機制,在退出之前。注意你的觀察,這看起來像是在做什麼(如果你的父進程死了,你的ppid被重新分配給init,即pid 1)

如果它有多個進程競爭在同一個監聽套接字上接受(同樣,不知道nginx是怎麼做到的,也許它有一個調度過程?),那麼你可以逐個替換它們,通過命令它們像上面那樣執行新程序,但是一次一個地執行,以至於永不放棄。請注意,在這樣的過程中,永遠不會有任何新的pid或父母/子女關係的變化。

至少,我想這可能是我如何做到的,離開我的頭頂。

相關問題