1. 程式人生 > >從nginx熱更新聊一聊Golang中的伺服器熱更新(上)

從nginx熱更新聊一聊Golang中的伺服器熱更新(上)

從nginx熱更新聊一聊Golang中的熱更新(上)

靜態語言在伺服器程式設計時都會遇到這樣的問題:如何保證已有的連線服務不中斷同時又升級版本?
最近花了點時間看了下nginx熱更新程式碼流程,想了下結合之前的經驗一併總結下熱更新

熱更新是什麼?

舉個例子,你現在在坐卡車,卡車開到了150KM/H

然後,有個輪胎,爆了

然後,司機說,你就直接換吧,我不停車。你小心點換

嗯。就這個意思

閘道器中的熱更新

服務程式熱更新這個問題在層7閘道器中尤其嚴重,閘道器中承載著大量的請求,包括HTTP/HTTPS短連線、HTTP/HTTPS長連線、甚至是websocket這種超長連線(websocket通常連線時間會很長,十幾分鍾到幾天不等)。服務程序熱更新是非常有必要的。

閘道器作為一個基礎元件,需要保證高可用,是很難將其先停下來再更新的;

有人說可以使用負載均衡將需要更新的元件先隔離,再停機更新,但是如果是一個很小的叢集沒有負載均衡呢,又或者這樣手動一臺一臺升級也著實麻煩,部分情況下就算隔離了也不過是不會有新的連線過來,舊的連線/請求依舊需要處理完成,否則就會造成部分服務不可用

不過實際上線上操作是叢集隔離加熱更新一起操作

nginx熱更新(Upgrading Executable on the Fly)

nginx [engine x]是Igor Sysoev編寫的一個HTTP和反向代理伺服器,另外它也可以作為郵件代理伺服器。 它已經在眾多流量很大的俄羅斯網站上使用了很長時間,這些網站包括Yandex、

Mail.Ru、VKontakte,以及Rambler。據Netcraft統計,在2012年8月份,世界上最繁忙的網站中有11.48%使用Nginx作為其伺服器或者代理伺服器。

NginX採用Master/Worker的多程序模型,Master程序負責整個NginX程序的管理。Nginx的模組化、熱更新、Http處理流程、日誌等機制都非常經典。這裡將會簡要介紹一下熱更新的機制

nginx熱升級流程

步驟1、升級nginx二進位制檔案,需要先將新的nginx可執行檔案替換原有舊的nginx檔案,然後給nginx master程序傳送USR2訊號,告知其開始升級可執行檔案;nginx master程序會將老的pid檔案增加.oldbin字尾,然後拉起新的master和worker程序,並寫入新的master程序的pid。

UID        PID  PPID  C STIME TTY          TIME CMD
root      4584     1  0 Oct17 ?        00:00:00 nginx: master process /usr/local/apigw/apigw_nginx/nginx
root     12936  4584  0 Oct26 ?        00:03:24 nginx: worker process
root     12937  4584  0 Oct26 ?        00:00:04 nginx: worker process
root     12938  4584  0 Oct26 ?        00:00:04 nginx: worker process
root     23692  4584  0 21:28 ?        00:00:00 nginx: master process /usr/local/apigw/apigw_nginx/nginx
root     23693 23692  3 21:28 ?        00:00:00 nginx: worker process
root     23694 23692  3 21:28 ?        00:00:00 nginx: worker process
root     23695 23692  3 21:28 ?        00:00:00 nginx: worker process

步驟2、在此之後,所有工作程序(包括舊程序和新程序)將會繼續接受請求。這時候,需要傳送WINCH訊號給nginx master程序,master程序將會向worker程序傳送訊息,告知其需要進行graceful shutdown,worker程序會在連線處理完之後進行退出。

UID        PID  PPID  C STIME TTY          TIME CMD
root      4584     1  0 Oct17 ?        00:00:00 nginx: master process /usr/local/apigw/apigw_nginx/nginx
root     12936  4584  0 Oct26 ?        00:03:24 nginx: worker process
root     12937  4584  0 Oct26 ?        00:00:04 nginx: worker process
root     12938  4584  0 Oct26 ?        00:00:04 nginx: worker process
root     23692  4584  0 21:28 ?        00:00:00 nginx: master process /usr/local/apigw/apigw_nginx/nginx
#若舊的worker程序還需要處理連線,則worker程序不會立即退出,需要待訊息處理完後再退出

步驟3、經過一段時間之後,將會只會有新的worker程序處理新的連線。

注意,舊master程序並不會關閉它的listen socket;因為如果出問題後,需要回滾,master程序需要法重新啟動它的worker程序。

步驟4、如果升級成功,則可以向舊master程序傳送QUIT訊號,停止老的master程序;如果新的master程序(意外)退出,那麼舊master程序將會去掉自己的pid檔案的.oldbin字尾。

nginx熱更新相關訊號

master程序相關訊號

USR2	升級可執行檔案
WINCH	優雅停止worker程序
QUIT	優雅停止master程序

worker程序相關訊號

TERM, INT	快速退出程序
QUIT	優雅停止程序

nginx相關程式碼走讀

1、USR2流程

在這裡插入圖片描述

master收到USR2訊號後,會拉起新的master nginx程序;

新的master程序拉起新的worker程序;

最終,老的worker程序和新的worker程序共用一個listen socket,接受連線

若打開了REUSEPORT開關,則socket繼承情況會有些區別,感興趣的可以自行翻看程式碼

2、WINCH流程

在這裡插入圖片描述

master程序收到WINCH訊號後,會給各個worker程序傳送QUIT訊號,讓其優雅退出;master程序並不再處理新的連線。

在這裡插入圖片描述

worker graceful shutdown流程,關閉listen socket,不再處理新的連線;待已有連線處理完後,清理連線,退出程序。

3、QUIT流程

master graceful shutdown流程,沒什麼好說的

nginx升級過程中若出現問題如何回滾?

nginx熱升級 QA

1、如何防止多次可執行檔案觸發熱更新?

相關程式碼
ngx_signal_handler
-->
case ngx_signal_value(NGX_CHANGEBIN_SIGNAL):
    if (ngx_getppid() == ngx_parent || ngx_new_binary > 0) {

        /*
            * Ignore the signal in the new binary if its parent is
            * not changed, i.e. the old binary's process is still
            * running.  Or ignore the signal in the old binary's
            * process if the new binary's process is already running.
            */

        action = ", ignoring";
        ignore = 1;
        break;
    }

    ngx_change_binary = 1;
    action = ", changing binary";
    break;

若老的nginx還在,nginx無法進行熱更新二進位制檔案

2、nginx升級過程中,發現新的可執行檔案出現問題該如何回滾?

  • a、向舊master程序傳送HUP訊號。舊程序將啟動新的worker程序,而且不會重新讀取配置。之後,通過向新的主master程序傳送QUIT訊號,可以優雅地關閉新的master和worker程序。
  • b、將TERM訊號傳送到新的master程序,然後新的master程序將向其worker程序傳送一條訊息,讓它們立即退出,這種退出不是graceful shutdown。當新的master程序退出時,舊的master程序將啟動新的worker程序。
  • c、如果新的程序沒有退出,則應該向它們傳送終止KILL訊號。當新的master程序退出時,舊的master程序將啟動新的工作程序。

3、什麼是graceful shutdown

本文中的graceful shutdown是指server不再處理新的連線,但是程序不會立即退出,待所有連線斷開後再退出程序。

總結一下個人在nginx 二進位制檔案熱升級時用的命令

cd /usr/local/nginx
cp nginx nginx_bak 
mv /data/nginx/nginx ./nginx #需要使用mv來更新二進位制檔案
./nginx -t #嘗試啟動,檢視其載入配置檔案等初始化功能是否正常

netstat -anp | grep -E "80|443" | grep nginx #檢查連線狀態
kill -USR2 `cat /usr/local/nginx/nginx.pid` #升級nginx可執行檔案,此時會有兩組nginx master和worker程序
kill -WINCH `cat /usr/local/nginx/nginx.pid.oldbin` #新的可執行檔案啟動ok,且能夠正常處理資料流,告知老的master程序去通知其worker程序進行優雅退出

...
kill -QUIT `cat /usr/local/nginx/nginx.pid.oldbin` #待所有的老的nginx worker程序優雅退出後(處理完連線),停止老的master程序

TODO:nginx還會有依賴的so檔案的熱升級–其實更應該屬於後臺程序的so檔案熱升級流程,我在使用它的時候也踩過坑–主要原因還是操作不規範,對so其載入執行原理不夠熟悉導致

熱升級

實際上,靜態語言後端server有一套固定的熱升級(單程序)流程,其基本流程如下:

若需要支援熱升級的是多程序,那麼nginx的熱升級過程是最值得參考的

  • 1、通過呼叫 fork/exec 啟動新的版本的程序,

  • 2、子程序呼叫介面獲取從父程序繼承的 socket 檔案描述符重新監聽 socket

  • 3、在此過程中,不會對使用者請求造成任何中斷。

nginx的熱升級流程也是類似,只不過由於nginx工作是多程序,故它會先啟動新版本的一組master/worker程序;

然後停止老的worker程序,讓其不處理連線,由新的worker程序來處理連線;

升級完畢後,即可退出老的master程序,熱升級完成。

參考