日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

java jar包 平滑重启,nginx 平滑重启的实现方法

發布時間:2023/12/4 编程问答 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java jar包 平滑重启,nginx 平滑重启的实现方法 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

一、背景

在服務器開發過程中,難免需要重啟服務加載新的代碼或配置,如果能夠保證server重啟的過程中服務不間斷,那重啟對于業務的影響可以降為0。最近調研了一下nginx平滑重啟,覺得很有意思,記錄下來供有興趣的同學查閱。

二、重啟流程

重啟意味著新舊接替,在交接任務的過程中勢必會存在新舊server并存的情形,因此,重啟的流程大致為:

啟動新的server

新舊server并存,兩者共同處理請求,提供服務

舊的server處理完所有的請求之后優雅退出

這里,最主要的問題在于如何保證新舊server可以并存,如果重啟前后的server端口一致,如何保證兩者可以監聽同一端口。

三、nginx實現

為了驗證nginx平滑重啟,筆者首先嘗試nginx啟動的情形下再次開啟一個新的server實例,結果如圖:

很明顯,重新開啟server實例是行不通的,原因在于新舊server使用了同一個端口80,在未開始socket reuseport選項復用端口時,bind系統調用會出錯。nginx默認bind重試5次,失敗后直接退出。而nginx需要監聽IPV4地址0.0.0.0和IPV6地址[::],故圖中打印出10條emerg日志。

接下來就開始嘗試平滑重啟命令了,一共兩條命令:

kill -USR2 `cat /var/run/nginx.pid`

kill -QUIT `cat /var/run/nginx.pid.oldbin`

第一條命令是發送信號USR2給舊的master進程,進程的pid存放在/var/run/nginx.pid文件中,其中nginx.pid文件路徑由nginx.conf配置。

第二條命令是發送信號QUIT給舊的master進程,進程的pid存放在/var/run/nginx.pid.oldbin文件中,隨后舊的master進程退出。

那么問題來了,為什么舊的master進程的pid存在于兩個pid文件之中?事實上,在發送信號USR2給舊的master進程之后,舊的master進程將pid重命名,原先的nginx.pid文件rename成nginx.pid.oldbin。這樣新的master進行就可以使用nginx.pid這個文件名了。

先執行第一條命令,結果如圖:

不錯,新舊master和worker進程并存了。 再來第二條命令,結果如圖:

如你所見,舊的master進程8527和其worker進程全部退出,只剩下新的master進程12740。

不由得產生困惑,為什么手動開啟一個新的實例行不通,使用信號重啟就可以達到。先看下nginx log文件:

除了之前的錯誤日志,還多了一條notice,意思就是繼承了sockets,fd值為6,7。 隨著日志翻看nginx源碼,定位到nginx.c/ngx_exec_new_binary函數之中,

ngx_pid_t

ngx_exec_new_binary(ngx_cycle_t *cycle, char *const *argv)

{

...

ctx.path = argv[0];

ctx.name = "new binary process";

ctx.argv = argv;

n = 2;

env = ngx_set_environment(cycle, &n);

...

var = ngx_alloc(sizeof(NGINX_VAR)

+ cycle->listening.nelts * (NGX_INT32_LEN + 1) + 2,

cycle->log);

...

p = ngx_cpymem(var, NGINX_VAR "=", sizeof(NGINX_VAR));

ls = cycle->listening.elts;

for (i = 0; i < cycle->listening.nelts; i++) {

p = ngx_sprintf(p, "%ud;", ls[i].fd);

}

*p = '\0';

env[n++] = var;

...

env[n] = NULL;

...

ctx.envp = (char *const *) env;

ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);

if (ngx_rename_file(ccf->pid.data, ccf->oldpid.data) == NGX_FILE_ERROR) {

...

return NGX_INVALID_PID;

}

pid = ngx_execute(cycle, &ctx);

if (pid == NGX_INVALID_PID) {

if (ngx_rename_file(ccf->oldpid.data, ccf->pid.data)

== NGX_FILE_ERROR)

{

...

}

}

...

return pid;

}

函數的流程為

將舊的master進程監聽的所有fd,拷貝至新master進程的env環境變量NGINX_VAR。

rename重命名pid文件

ngx_execute函數fork子進程,execve執行命令行啟動新的server。

在server啟動流程之中,涉及到環境變量NGINX_VAR的解析,ngx_connection.c/ngx_add_inherited_sockets具體代碼為:

static ngx_int_t

ngx_add_inherited_sockets(ngx_cycle_t *cycle)

{

...

inherited = (u_char *) getenv(NGINX_VAR);

if (inherited == NULL) {

return NGX_OK;

}

if (ngx_array_init(&cycle->listening, cycle->pool, 10,

sizeof(ngx_listening_t))

!= NGX_OK)

{

return NGX_ERROR;

}

for (p = inherited, v = p; *p; p++) {

if (*p == ':' || *p == ';') {

s = ngx_atoi(v, p - v);

...

v = p + 1;

ls = ngx_array_push(&cycle->listening);

if (ls == NULL) {

return NGX_ERROR;

}

ngx_memzero(ls, sizeof(ngx_listening_t));

ls->fd = (ngx_socket_t) s;

}

}

...

ngx_inherited = 1;

return ngx_set_inherited_sockets(cycle);

}

函數流程為:

解析環境變量NGINX_VAR的值,獲取fd存入數組

fd對應的socket設為ngx_inherited,保存這些socket的信息。

也就是說,新的server壓根就沒重新bind端口listen,這些fd狀態和值都是新的master進程fork時帶過來的,新的master進程監聽處理繼承來的文件描述符即可,這里比較關鍵的一點在于listen socket文件描述符通過ENV傳遞。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。

總結

以上是生活随笔為你收集整理的java jar包 平滑重启,nginx 平滑重启的实现方法的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。