• 售前

  • 售后

热门帖子
入门百科

nginx 平滑重启的实现方法

[复制链接]
楚一帆 显示全部楼层 发表于 2021-10-26 12:45:55 |阅读模式 打印 上一主题 下一主题
一、背景
在服务器开发过程中,难免必要重启服务加载新的代码或设置,如果能够保证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日志。
接下来就开始尝试平滑重启下令了,一共两条下令:
  1. kill -USR2 `cat /var/run/nginx.pid`
  2. 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函数之中,
  1. ngx_pid_t
  2. ngx_exec_new_binary(ngx_cycle_t *cycle, char *const *argv)
  3. {
  4.   ...
  5.   ctx.path = argv[0];
  6.   ctx.name = "new binary process";
  7.   ctx.argv = argv;
  8.   n = 2;
  9.   env = ngx_set_environment(cycle, &n);
  10. ...
  11.   var = ngx_alloc(sizeof(NGINX_VAR)
  12.           + cycle->listening.nelts * (NGX_INT32_LEN + 1) + 2,
  13.           cycle->log);
  14. ...
  15.   p = ngx_cpymem(var, NGINX_VAR "=", sizeof(NGINX_VAR));
  16.   ls = cycle->listening.elts;
  17.   for (i = 0; i < cycle->listening.nelts; i++) {
  18.     p = ngx_sprintf(p, "%ud;", ls[i].fd);
  19.   }
  20.   *p = '\0';
  21.   env[n++] = var;
  22. ...
  23.   env[n] = NULL;
  24. ...
  25.   ctx.envp = (char *const *) env;
  26.   ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);
  27.   if (ngx_rename_file(ccf->pid.data, ccf->oldpid.data) == NGX_FILE_ERROR) {
  28.     ...
  29.     return NGX_INVALID_PID;
  30.   }
  31.   pid = ngx_execute(cycle, &ctx);
  32.   if (pid == NGX_INVALID_PID) {
  33.     if (ngx_rename_file(ccf->oldpid.data, ccf->pid.data)
  34.       == NGX_FILE_ERROR)
  35.     {
  36.       ...
  37.     }
  38.   }
  39. ...
  40.   return pid;
  41. }
复制代码
函数的流程为
      
  • 将旧的master历程监听的全部fd,拷贝至新master历程的env环境变量NGINX_VAR。  
  • rename重定名pid文件  
  • ngx_execute函数fork子历程,execve实行下令行启动新的server。  
  • 在server启动流程之中,涉及到环境变量NGINX_VAR的分析,ngx_connection.c/ngx_add_inherited_sockets具体代码为:
  1. static ngx_int_t
  2. ngx_add_inherited_sockets(ngx_cycle_t *cycle)
  3. {
  4. ...
  5.   inherited = (u_char *) getenv(NGINX_VAR);
  6.   if (inherited == NULL) {
  7.     return NGX_OK;
  8.   }
  9.   if (ngx_array_init(&cycle->listening, cycle->pool, 10,
  10.             sizeof(ngx_listening_t))
  11.     != NGX_OK)
  12.   {
  13.     return NGX_ERROR;
  14.   }
  15.   for (p = inherited, v = p; *p; p++) {
  16.     if (*p == ':' || *p == ';') {
  17.       s = ngx_atoi(v, p - v);
  18.       ...
  19.       v = p + 1;
  20.       ls = ngx_array_push(&cycle->listening);
  21.       if (ls == NULL) {
  22.         return NGX_ERROR;
  23.       }
  24.       ngx_memzero(ls, sizeof(ngx_listening_t));
  25.       ls->fd = (ngx_socket_t) s;
  26.     }
  27.   }
  28.   ...
  29.   ngx_inherited = 1;
  30.   return ngx_set_inherited_sockets(cycle);
  31. }
复制代码
函数流程为:
分析环境变量NGINX_VAR的值,获取fd存入数组
fd对应的socket设为ngx_inherited,生存这些socket的信息。
也就是说,新的server压根就没重新bind端口listen,这些fd状态和值都是新的master历程fork时带过来的,新的master历程监听处理继承来的文件形貌符即可,这里比较关键的一点在于listen socket文件形貌符通过ENV通报。
以上就是本文的全部内容,希望对各人的学习有所资助,也希望各人多多支持草根技术分享。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x

帖子地址: 

回复

使用道具 举报

分享
推广
火星云矿 | 预约S19Pro,享500抵1000!
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

草根技术分享(草根吧)是全球知名中文IT技术交流平台,创建于2021年,包含原创博客、精品问答、职业培训、技术社区、资源下载等产品服务,提供原创、优质、完整内容的专业IT技术开发社区。
  • 官方手机版

  • 微信公众号

  • 商务合作