A method to achieve a smooth restart nginx

One, background

In the Server development process, we will inevitably need to restart the service to load the new code or configuration, if the server to ensure uninterrupted service during the restart, restart it for business impact can be reduced to zero. Recent research a bit nginx GR find it very interesting, recorded for interested students to view.

Second, the restart process

  •  Restart the old and new means to succeed, there is a case of old and new server is bound to coexist during the transition task, therefore, the restart process is roughly:
    • Start a new server
    • The coexistence of old and new server, which together handle requests to provide services
    • The old server After processing all requests elegant exit
  • Here, the main problem is how to ensure that the old and new server can co-exist, if the same server port before and after the restart, how to ensure that both could listen on the same port.

 Three, nginx achieve

To verify nginx GR, I first attempt to open the case nginx start a new instance of the server again, the results shown:

Clearly, re-open the server instance is not feasible, because the old and new server uses a port with 80, did not start in the socket reuseport option multiplexing port, bind system call error. nginx default bind to retry five times, direct exit after the failure. Nginx IPV4 address and the need to monitor and IPV6 addresses 0.0.0.0 [::], so that the FIG. 10 print log emerg.

Then he began to try to smooth restart command a total of two commands:

?
1
2
kill -USR2 ` cat /var/run/nginx .pid`
kill -QUIT ` cat /var/run/nginx .pid.oldbin`

The first command signal is sent to the old master process USR2, pid /var/run/nginx.pid stored in the process file, the file path which is configured by nginx.pid nginx.conf.

The second command is to send QUIT signal to the old master process, pid process /var/run/nginx.pid.oldbin stored in the file, then the old master process exits.

So the question is, why pid old master process exists in two pid file? In fact, after sending a signal to the old master USR2 process, the old master process pid rename the original file rename nginx.pid into nginx.pid.oldbin. Such were the new master can use the file name of the nginx.pid.

First executing the first command, the results shown:

Yes, the coexistence of old and new master and the worker process. The second command again, the results shown:

As you can see, the old master process 8527 and its worker processes all quit, leaving only the new master process 12740.

Could not help but confused as to why manually open a new instance does not work, use the restart signal can be achieved. Look under nginx log file:

In addition to the error log before, more than a notice, meaning that inherit sockets, fd value of 6,7. As the log look nginx source positioned into nginx.c / ngx_exec_new_binary function,

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
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;
}

Process function is

  1.  All fd old master process monitor, and copied to the new master process env environment variable NGINX_VAR.
  2. rename rename file pid
  3. ngx_execute function fork child process, execve execute the command line to start the new server.
  4. Among the server startup process, involves parsing the environment variable NGINX_VAR, ngx_connection.c / ngx_add_inherited_sockets specific code is:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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传递。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

Guess you like

Origin www.cnblogs.com/zhuyeshen/p/12061495.html