从nginx热更新聊一聊C++/Golang中的热更新(上)
从nginx热更新聊一聊C++/Golang中的热更新(上)
静态语言在服务器编程时都会遇到这样的问题:如何保证已有的连接服务不中断同时又升级版本?
最近花了点时间看了下nginx热更新代码流程,想了下结合之前的经验一并总结下热更新
服务程序热更新这个问题在层7网关中尤其严重,网关中承载着大量的请求,包括HTTP/HTTPS短连接、HTTP/HTTPS长连接、甚至是websocket这种超长连接(websocket通常连接时间会很长,十几分钟到几天不等)。服务进程热更新是非常有必要的。
nginx热更新(Upgrading Executable on the Fly)
nginx [engine x]是Igor Sysoev编写的一个HTTP和反向代理服务器,另外它也可以作为邮件代理服务器。 它已经在众多流量很大的俄罗斯网站上使用了很长时间,这些网站包括Yandex、Mail.Ru、VKontakte,以及Rambler。据Netcraft统计,在2012年8月份,世界上最繁忙的网站中有11.48%使用Nginx作为其服务器或者代理服务器。
NginX采用Master/Worker的多进程模型,Master进程负责整个NginX进程的管理。Nginx的模块化、热更新、Http处理流程、日志等机制都非常经典。这里将会简要介绍一下热更新的机制
nginx热升级流程
步骤1、升级nginx二进制文件,需要先将新的nginx可执行文件替换原有旧的nginx文件,然后给nginx master进程发送USR2信号,告知其开始升级可执行文件;nginx master进程会将老的pid文件增加.oldbin后缀,然后拉起新的master和worker进程,并写入新的master进程的pid。
PID PPID USER %CPU VSZ WCHAN COMMAND
33126 1 root 0.0 1164 pause nginx: master process /usr/local/nginx/sb
33134 33126 nobody 0.0 1368 kqread nginx: worker process (nginx)
33135 33126 nobody 0.0 1380 kqread nginx: worker process (nginx)
33136 33126 nobody 0.0 1368 kqread nginx: worker process (nginx)
36264 33126 root 0.0 1148 pause nginx: master process /usnx)
步骤2、在此之后,所有工作进程(包括旧进程和新进程)将会继续接受请求。这时候,需要发送WINCH信号给nginx master进程,master进程将会向worker进程发送消息,告知其需要进行graceful shutdown,worker进程会在连接处理完之后进行退出。
PID PPID USER %CPU VSZ WCHAN COMMAND
33126 1 root 0.0 1164 pause nginx: master process /usr/local/nginx/sb
33135 33126 nobody 0.0 1380 kqread nginx: worker process is shutting down (n
36264 33126 root 0.0 1148 pause nginx: master process /usr/local/nginx/sb
36265 36264 nobody 0.0 1364 kqread nginx: worker process (nginx)
36266 36264 nobody 0.0 1364 kqread nginx: worker process (nginx)
36267 36264 nobody 0.0 1364 kqread nginx: worker process (nginx)
步骤3、经过一段时间之后,将会只会有新的worker进程处理新的连接。
注意,旧master进程并不会关闭它的监听socket;因为如果出问题后,需要回滚,master进程需要法重新启动它的worker进程。
步骤4、如果升级成功,则可以向旧master进程发送QUIT信号,停止老的master进程;如果新的master进程(意外)退出,那么旧master进程将会去掉自己的pid文件的.oldbin后缀。
nginx热更新相关信号
USR2 升级可执行文件
WINCH 优雅停止worker进程
QUIT 优雅停止master进程
nginx相关代码走读
1、USR2流程
master收到USR2进程后,会拉起新的master nginx进程
nginx升级过程中若出现问题如何回滚?
nginx热升级 QA
1、如何防止多次可执行文件触发热更新?
相关代码
ngx_signal_handler
-->
case ngx_signal_value(NGX_CHANGEBIN_SIGNAL):
if (ngx_getppid() == ngx_parent || ngx_new_binary > 0) {
/*
* Ignore the signal in the new binary if its parent is
* not changed, i.e. the old binary's process is still
* running. Or ignore the signal in the old binary's
* process if the new binary's process is already running.
*/
action = ", ignoring";
ignore = 1;
break;
}
ngx_change_binary = 1;
action = ", changing binary";
break;
若老的nginx还在,nginx无法进行热更新二进制文件
2、nginx升级过程中,发现新的可执行文件出现问题该如何回滚?
- a、向旧master进程发送HUP信号。旧进程将启动新的worker进程,而且不会重新读取配置。之后,通过向新的主master进程发送QUIT信号,可以优雅地关闭新的master和worker进程。
- b、将TERM信号发送到新的master进程,然后新的master进程将向其worker进程发送一条消息,让它们立即退出,这种退出不是graceful shutdown。当新的master进程退出时,旧的master进程将启动新的worker进程。
- c、如果新的进程没有退出,则应该向它们发送终止KILL信号。当新的master进程退出时,旧的master进程将启动新的工作进程。
3、什么是graceful shutdown
本文中的graceful shutdown是指server不再处理新的连接,但是进程不会立即退出,待所有连接断开后再退出进程。
热升级
实际上,静态语言后端server有一套固定的热升级(单进程)流程,其基本流程如下:
若需要支持热升级的是多进程,那么nginx的热升级过程是最值得参考的
-
1、通过调用 fork/exec 启动新的版本的进程,
-
2、子进程调用接口获取从父进程继承的 socket 文件描述符重新监听 socket
-
3、在此过程中,不会对用户请求造成任何中断。