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日志。
接下来就开始尝试平滑重启命令了,一共两条命令:
|
1
2
|
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函数之中,
|
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_tngx_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;} |
函数的流程为
- 将旧的master进程监听的所有fd,拷贝至新master进程的env环境变量NGINX_VAR。
- rename重命名pid文件
- ngx_execute函数fork子进程,execve执行命令行启动新的server。
- 在server启动流程之中,涉及到环境变量NGINX_VAR的解析,ngx_connection.c/ngx_add_inherited_sockets具体代码为:
|
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_tngx_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传递。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。
nginx 平滑重启的实现方法的更多相关文章
- nginx平滑重启与平滑升级的方法
如何实现nginx平滑重启与平滑升级? 平滑重启 kill -HUP `cat /usr/local/www/nginx/logs/nginx.pid` 平滑升级nginx: cd /yujialin ...
- 【学习笔记】启动Nginx、查看nginx进程、查看nginx服务主进程的方式、Nginx服务可接受的信号、nginx帮助命令、Nginx平滑重启、Nginx服务器的升级
1.启动nginx的方式: cd /usr/local/nginx ls ./nginx -c nginx.conf 2.查看nginx的进程方式: [root@localhost nginx] ...
- 启动Nginx、查看nginx进程、nginx帮助命令、Nginx平滑重启、Nginx服务器的升级
1.启动nginx的方式: cd /usr/local/nginx ls
- Apache和Nginx平滑重启
之前修改了服务器配置都是简单粗暴的用restart重启apache/nginx,据说这样不好.需要平滑重启服务器,避免重启时打断用户行为.然后就根据官方文档了解了一下平滑重启的命令.本文根据Apach ...
- Nginx平滑重启
kill -HUP cat /usr/local/nginx/logs/nginx.pid`
- nginx重启几种方法
http://blog.csdn.net/zqinghai/article/details/71125045 ps -ef|grep nginx 平滑重启命令: kill -HUP 住进称号或进程号文 ...
- Nginx常用的平滑重启
之前在做运维工作中,经常需要添加虚拟主机,或者添加修改配置文件,但是测试环境还好,随便玩,如果是生产环境的话,既要保证配置不出问题,有不能中断服务.如果是这样的话,就需要对配置文件进行语法检测以及平滑 ...
- Nginx 的启动、停止、平滑重启、信号控制和平滑升级
Nginx 的启动 假设 nginx 安装在 /usr/local/nginx 目录中,那么启动 nginx 的命令就是: [root@localhost ~]# /usr/local ...
- Nginx 启动、停止、平滑重启和平滑升级 graceful shutdown of worker processes
Controlling nginx http://nginx.org/en/docs/control.html nginx can be controlled with signals. The pr ...
随机推荐
- 【开发笔记】- Java中关于HashMap的元素遍历的顺序问题
今天在使用如下的方式遍历HashMap里面的元素时 for (Entry<String, String> entry : hashMap.entrySet()) { MessageForm ...
- 冠捷显示成功的信息化建设(MES应用案例)
企业介绍 冠捷科技集团是驰誉全球的大型高科技跨国企业,产品包括彩色显示器( CRT monitor ).液晶显示器( LCD monitor ).液晶电视( LCD-TV )与等离子电视( PDP ) ...
- SpringBoot整合定时任务和异步任务处理
SpringBoot定时任务schedule讲解 简介:讲解什么是定时任务和常见定时任务区别 1.常见定时任务 Java自带的java.util.Timer类 timer:配置比较麻烦,时间延后问题, ...
- Flask入门到放弃(四)—— 数据库
转载请在文章开头附上原文链接地址:https://www.cnblogs.com/Sunzz/p/10979970.html 数据库操作 ORM ORM 全拼Object-Relation Mappi ...
- MySQL存储过程02
这次接着说MySQL存储过程: 我们先看它的多分支控制结构case: case的语句很简单: case 变量名 when 条件1 then 输出结果1; when 条件2 then 输出结果2; .. ...
- JanusGraph多图配置 (cassandra)
JanusGraph多图配置目的 :一个端口开启后可根据句柄操作多个图 .(cassandra存储后端) 1.GremlinServer多图配置 服务器gremlin-server.yaml中可以设置 ...
- 团队——Alpha版本发布
这个作业属于哪个课程 课程链接 这个作业要求在哪里 作业要求的链接 团队名称 杨荣模杰和他的佶祥虎 这个作业的目标 发布并说明产品Alpha版本 一.团队成员的学号姓名列表 学号 姓名 2017311 ...
- Improving Sequential Recommendation with Knowledge-Enhanced Memory Networks(知识图谱)
本文作者:杨昆霖,2015级本科生,目前研究方向为知识图谱,推荐系统,来自中国人民大学大数据管理与分析方法研究北京市重点实验室. 引言 经常上购物网站时,注意力会被首页上的推荐吸引过去,往往本来只想买 ...
- Python+Appium学习之启动手机APP或者浏览器
一.启动浏览器:pycharm中python脚本如下: from appium import webdriver desired_caps ={ 'platformName':'Android', ' ...
- HRBUST 2040 二叉树的遍历
给出一棵二叉树的中序和前序遍历,输出它的后序遍历. Input 本题有多组数据,输入处理到文件结束. 每组数据的第一行包括一个整数n,表示这棵二叉树一共有n个节点. 接下来的一行每行包括n个整数,表示 ...