nginx实现热升级的原理

nginx 启停操作

  sudo ./nginx -c /usr/local/src/nginx/conf/nginx.conf  //启动
  sudo /usr/local/src/nginx/sbin/nginx -s reload //平滑重启

  ./nginx -s stop #立即停止服务
  ./nginx -s quit #优雅的停止服务
  ./nginx -s reload #重载配置文件
  ./nginx -s reopen #重新开始记录日志文件

  停止:
  ps -ef|grep nginx
  kill -QUIT 2072 //停止

  kill -TERM 2132
  kill -9 nginx //强制停止

  重启:
  kill -HUP 进程号

热升级简介

  • 热升级大致分为两步

    1. 编译好Nginx的二进制文件

    2. 通过信号,完成新老进程的平滑过渡,保证升级期间服务可用。

热升级步骤

第一步:先查看原先编译的参数。-V可以查看编译时的参数(-v时查看版本)。在新编译时,要将原有的模块参数也加上,否则原有的模块不会编译进去。

第二步:开始编译,编译过程和上篇一致,增加了要添加模块的参数。

  • 通过 -–add-module可以编译第三方的模块到Nginx。 这里只需要make编译,不要make install安装。

  • 老版的用 --add-module,新版的用 --add-dynamic-module

  • 编译完成后的nginx二进制文件在objs目录。

第三步:备份和替换

第四步:热升级。至此我们只是替换了二进制文件,但是现在服务中的Nginx进程还是由原来的nginx二进制文件启动的,所以请求还是走原有的逻辑。

  • 此时所有的请求都会平滑过渡到新的worker进程。但是旧的master进程13195还在,只是没有worker进程,如果要回退,只需要拉回旧的nginx拉回worker进程。如果运行一段时间没有问题,可以通过kill -QUIT 13159彻底关闭老进程

回滚

  • 将原来备份好旧版本的二进制文件的再复制回去

热升级流程

1. 将旧Nginx文件换成新的Nginx文件(注意备份)

2. 像master进程发送USER2信号会执行下面的动作:

  • master进程修改/logs/nginx.pid文件名(该文件是用来记录启动Nginx后的master进程号),给其加后缀.obin。因为新起的进程会生成一个nginx.pid文件,所以会修改原有文件的名称。这样做可以在回滚时找到对应的进程号。

  • master进程用新Nginx文件启动新的master进程

    1. 发送WINCH信号,关闭原有旧worker进程

    2. 向老进程发送QUIT信号,关闭老master,这时候进程退出(3,4步也可以合成4一步,这样在关闭老master进程时,老master进程会通知自己的worker进程关闭)

    3. 如果需要回滚:向老master发送HUP,向新master发送QUIT

信号

  • 其中发送信号的方式有两种

    1. 通过 kill -HUP 12392这种方式直接向进程发送信号。

    2. 通过nginx命令行方式:nginx -s reload

  • 第二种实际上就是利用logs目录下的nginx.pid读取进程id然后发送对于的信号,本质一样。

  • 上面红色标识的信号只能通过kill -命令直接发送给对应进程。而没有对应的nginx命令。

reload流程(热重启)

  1. 向master进程发送HUP信号(等同reload命令)

  2. master进程校验配置语法是否正确

  3. master进程打开新的监听端口,

  4. master进程用新配置启动新的worker子进程

  5. master进程向老worker子进程发送QUIT信号

  6. 老worker进程关闭监听句柄,处理完当前连接后结束进程

  7. 说明:

    • 在新的配置文件里,如果我们监听了新的端口,master会打开这个监听端口,以便新起的worker进程进行监听和任务处理

    • 老worker进程正常情况下,在处理已连接的请求后,会优雅退出,有时会出现老worker进程长时间存在,但是也只是影响已经建立的连接。

监听句柄

  • 即调用过listen的socket。

  • 子进程会自动共享父进程被listen的socket。句柄可以理解就是 socket,或者叫fd文件句柄。

集群问题

  • 如果是频繁修改upstream集群信息,那么不建议使用reload方式,你的修改目标非常简单明确,而reload是重新对所有配置生效,建议使用openresty实现API服务,由API来直接修改upstream信息。

端口问题

  • 新配置中关闭的端口,reload之后也不会关闭,因为master进程不会关闭端口。

SIGHUP

  • 把cycle对象传递给ngx_init_cycle(),创建一个新的cycle对象,完全复制老cycle,但更新了配置文件,随用ngx_cycle全局指针切换到新的cycle对象,完全reload。

最后更新于

这有帮助吗?