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;
}

函数的流程为

  1. 将旧的master进程监听的所有fd,拷贝至新master进程的env环境变量NGINX_VAR。
  2. rename重命名pid文件
  3. ngx_execute函数fork子进程,execve执行命令行启动新的server。
  4. 在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传递。

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

(0)

相关推荐

  • nginx 平滑重启与升级的实现方法

    如果改变了Nginx的配置文件(nginx.conf),想重启Nginx,同样可以通过发送系统信号给Nginx主进程的方式来进行.不过,重启之前,要确认Nginx配置文件(nginx.conf)的语法是正确的,否则Nginx将不会加载新的配置文件.通过以下命令可以判断Nginx配置文件是否正确: /usr/local/webserver/nginx/sbin/nginx    -t  -c/usr/local/webserver/nginx/conf/nginx.conf 如果配置文件不正确,屏

  • nginx 平滑重启的实现方法

    一.背景 在服务器开发过程中,难免需要重启服务加载新的代码或配置,如果能够保证server重启的过程中服务不间断,那重启对于业务的影响可以降为0.最近调研了一下nginx平滑重启,觉得很有意思,记录下来供有兴趣的同学查阅. 二.重启流程 重启意味着新旧接替,在交接任务的过程中势必会存在新旧server并存的情形,因此,重启的流程大致为: 启动新的server 新旧server并存,两者共同处理请求,提供服务 旧的server处理完所有的请求之后优雅退出 这里,最主要的问题在于如何保证新旧serv

  • nginx平滑重启和平滑升级的图文教程

    目录 nginx平滑重启 平滑重启主要流程 图解 nginx平滑升级 平滑升级主要流程 图解 总结 nginx平滑重启 如果修改了nginx的配置文件(nginx.conf),想要重启nginx,可以发送系统信号给Nginx的master进程来进行. 平滑重启主要流程 向master进程发送HUB信号(reload命令) nginx -s reload kill -s gighup Nginx主进程号 master进程检查配置语法是否正确 如果配置文件或者语法错误会出现下图代码 master打开

  • 使用 Nginx 部署静态页面的方法

    Nginx 介绍 Nginx 是俄罗斯人编写的十分轻量级的 HTTP 服务器, Nginx,它的发音为 " engine X ",是一个高性能的 HTTP 和反向代理服务器,同时也是一个 IMAP/ POP3/ SMTP 代理服务器.Nginx 是由俄罗斯人 Igor Sysoev 为俄罗斯访问量第二的 Rambler.ru 站点开发的,它已经在该站点运行超过两年半了.Igor Sysoev 在建立的项目时,使用基于 BSD 许可. 英文主页:http://nginx.net. Ngi

  • Nginx 出现 403 Forbidden 最终解决方法

    Nginx 出现 403 Forbidden 最终解决 步骤一: 检查目录权限.权限不足的就加个权限吧. 例子:chmod -R 755 / var/www 步骤二: 打开nginx.conf 例子:vim /etc/nginx/nginx.conf 把 user 用户名 改为 user root 或 其它有高权限的用户名称即可 步骤三 如果是centos,看一下selinux是否关闭了 查看SELinux状态: 1./usr/sbin/sestatus -v      ##如果SELinux

  • Nginx下ThinkPHP5的配置方法详解

    本文主要给大家介绍了关于Nginx下ThinkPHP5的配置方法,分享出来供大家参考学习,下面话不多说,来一起看看详细的介绍: url里public目录的隐藏 出于安全的考虑,TP5的入口文件改成放在public下了,因为这样的话能防止被恶意用户访问到"/thinkphp/"."/vendor/"等等这些目录下的文件.所以当你以之前的习惯将网站documentroot配置为项目根目录的时候就会需要在url后面加上/public/来访问.当然可能也会有童鞋把入口文件放

  • 详解linux中nginx启动 重启 关闭命令

    启动操作 nginx -c /usr/local/nginx/conf/nginx.conf -c参数指定了要加载的nginx配置文件路径 停止操作 停止操作是通过向nginx进程发送信号来进行的 步骤1:查询nginx主进程号 ps -ef | grep nginx 在进程列表里 面找master进程,它的编号就是主进程号了. 步骤2:发送信号 从容停止Nginx: kill -QUIT 主进程号 例如:kill -QUIT 16391 快速停止Nginx: kill -TERM 主进程号 强

  • Nginx 服务器重启关闭重新加载命令

    nginx -s reload  :修改配置后重新加载生效 nginx -s reopen  :重新打开日志文件 nginx -t  测试nginx配置文件是否正确 检查正确性时,出现: nginx: the configuration file /home/kratos/local/nginx/conf/nginx.conf syntax is ok nginx: [emerg] bind() to 0.0.0.0:80 failed (13: Permission denied) nginx

  • ubuntu14.04LTS安装nginx+mariaDB+php7+YAF的方法

    本文讲述了ubuntu14.04LTS安装nginx+mariaDB+php7+YAF的方法.分享给大家供大家参考,具体如下: ubuntu apt-get方式安装nginx 参考: http://nginx.org/en/linux_packages.html 首先 in order to authenticate the nginx repository signature and to eliminate warnings about missing PGP key during inst

  • 升级nginx以支持http2的方法

    本文介绍了升级nginx以支持http2的方法,分享给大家,具体如下: 一键升级脚本 https://github.com/whisshe/upHttp2.git HTTP/2 的优势 相比 HTTP/1.x,HTTP/2 在底层传输做了很大的改动和优化: HTTP/2 采用二进制格式传输数据,而非 HTTP/1.x 的文本格式.二进制格式在协议的解析和优化扩展上带来更多的优势和可能. HTTP/2 对消息头采用 HPACK 进行压缩传输,能够节省消息头占用的网络的流量.而 HTTP/1.x 每

随机推荐