如何優雅地關閉worker進程?

  • 2019 年 12 月 24 日
  • 筆記

之前我們講解 Nginx 命令行的時候,可以看到 Nginx 停止有兩種方式,分別是 nginx -s quitnginx -s stop,其中 stop 是指立即停止 Nginx,而 quit 是指優雅的關閉 Nginx,對應的訊號也是同樣的,還有我們之前提到的 reload 和熱升級這樣的過程中都涉及到了優雅的停止 Nginx。

那所謂的優雅的停止 Nginx 究竟是怎樣一個過程呢,接下來讓我一起來學習下吧。

何為優雅的關閉?

所謂的優雅的關閉,是針對 worker 進程而言的,因為只有 worker 進程 才會處理請求。如果我們在處理一個連接的時候,不管連接此時對於請求是怎樣一個作用,直接去關閉鏈接會導致用戶收到錯誤,所以優雅地關閉就是指 Nginx 的 worker 進程 可以識別出當前連接沒有正在處理請求,這個時候再把連接進行關閉。

對於某些請求 Nginx 無法做到優雅地關閉 worker 進程,比如當 Nginx 代理 websocket 協議的時候,在 websocket 後面進行通訊的 frame 楨裡面,Nginx 是不解析他的楨的;Nginx 做 TCP 層或者 UDP 層反向代理的時候,也沒有辦法識別一個請求需要經歷多少報文才算是結束;但是對於 HTTP 請求,Nginx 可以做到,所以優雅地關閉主要針對的是 HTTP 請求。

接下來我們去看一下優雅地關閉 worker 進程都有哪些流程。

優雅的關閉流程

首先第一步會設置一個定時器,在 nginx.conf 中可以配置一個 worker_shutdown_timeout,配置完 worker_shutdown_timeout 之後,會加一個標誌位,表示進入優雅關閉流程了。

第二步會先關閉監聽句柄,要保證所在的 worker 進程不會再去處理新的連接。

接下來會先去看連接池,因為 Nginx 為了保證對資源的利用是最大化的,經常會保存一些空閑的連接,但是沒有斷開,這時候會首先關閉空閑連接。

第四步是可能非常耗時的一步,因為 Nginx 不是主動的立刻關閉,是通過第一步添加的標誌位,然後在循環中每當發現一個請求處理完畢,就會把這個請求使用的連接關掉,所以在循環中等待關閉所有的時間可能會很長。當設置了 worker_shutdown_timeout 的時候,即使請求還沒處理完,當時間到了之後這些請求都會被強制關閉,也就是說優雅地關閉只完成了一半,有一部分連接是立即停止的。

因此在以下兩個條件:當所有循環中連接被優雅地關閉,或者達到了 worker_shutdown_timeout 時間定時器以後,worker 進程都會立即退出。

總結

這篇文章主要講解了 worker 進程優雅關閉的一個過程,很多時候我們都會用到 Nginx 優雅關閉這樣一個特性,那麼在這一個特性失效的時候,我們需要考慮 Nginx 有沒有能力去判定一個連接此時應當被正確的關掉;或者說如果出現了錯誤、有些模組或者有些客戶端不能正常的處理請求時,Nginx 需要有一些例外的措施,比如 worker_shutdown_timeout 來保證 Nginx 老的 worker 進程可以正常的退出掉。