nginx变量引发的故障

线上出现个故障,一台nginx会有502报出。可也不全是是502,而其他nginx却没有问题。 上去ps看了下,发现有个进程居然是刚启动的。这里也看到我们使用了dyups做nginx的服务发现。

1
2
3
4
5
6
timo    770  2.0  0.1 219016 13520 ?        Ss   Jan19 4233:48 /usr/bin/python /data/apps/opt/nginx/sbin/dyups_nginx.py
root 848 0.0 0.1 778708 14112 ? Ss Jan19 0:02 nginx: master process /data/apps/opt/nginx/sbin/nginx
timo 16002 0.8 4.5 1010392 348068 ? S May11 443:28 nginx: worker process
timo 16004 4.2 16.4 2114232 1268584 ? S May11 2132:53 nginx: worker process
timo 16005 2.4 15.2 2077140 1179936 ? S May11 1223:14 nginx: worker process
timo 26205 7.2 3.2 997208 253672 ? S 15:50 0:56 nginx: worker process

看了下操作系统日志,发现果然在50分35秒产生了一个oom的。而这个时间相当短,导致整个系统都没有内存使用的报警。而情况是当时有非常大的突发请求。

1
2
3
4
5
6
7
8
Jun 15 15:50:35 ugp-nginx-server1 kernel: nginx invoked oom-killer: gfp_mask=0x2840d0, order=0, oom_score_adj=0
Jun 15 15:50:35 ugp-nginx-server1 kernel: nginx cpuset=/ mems_allowed=0
Jun 15 15:50:35 ugp-nginx-server1 kernel: CPU: 3 PID: 16004 Comm: nginx Tainted: G OE ------------ 3.10.0-957.21.3.el7.x86_64 #1
Jun 15 15:50:35 ugp-nginx-server1 kernel: Hardware name: Alibaba Cloud Alibaba Cloud ECS, BIOS 8c24b4c 04/01/2014


Jun 15 15:50:35 ugp-nginx-server1 kernel: Out of memory: Kill process 16003 (nginx) score 139 or sacrifice child
Jun 15 15:50:35 ugp-nginx-server1 kernel: Killed process 16003 (nginx) total-vm:2070420kB, anon-rss:972728kB, file-rss:64kB, shmem-rss:102480kB

那么问题来了,worker进程重新启动会重新加载配置文件吗? 当然不会,看了下错误日志,这个后端在现在的环境里居然没有。

1
2021/06/15 15:50:39 [error] 26205#0: *5894957487 connect() failed (111: Connection refused) while connecting to upstream, client: 100.122.78.91, server: rest2.timoq.com, request: "POST /1234567809/timo/token HTTP/1.1", upstream: "http://172.16.65.239:31786/1234567809/timo/token", host: "rest2.timoq.com"

特地去对应机器上查看了下,最后一次更新还是5月19日的。这都快一个月了。而nginx上次reload时间是5月11日,因此几乎可以断定,worker里读取的是master进程reload的时候的变量数据。

1
2
3
4
5
6
7
8
9
10
$ ll -tr
-rw-r--r-- 1 timo timo 3195026 May 19 12:00 rest2-31786-log.log.2021-05-19-11.gz
-rw-r--r-- 1 timo timo 1874155 May 19 12:00 rest2-31786-error.log.2021-05-19-11.gz
-rw-r--r-- 1 timo timo 1040719 May 19 13:00 rest2-31786-log.log.2021-05-19-12.gz
-rw-r--r-- 1 timo timo 8416 May 19 13:01 rest2-31786-error.log.2021-05-19-12.gz
-rw-r--r-- 1 timo timo 1076004 May 19 14:00 rest2-31786-log.log.2021-05-19-13.gz
-rw-r--r-- 1 timo timo 7012 May 19 14:02 rest2-31786-error.log.2021-05-19-13.gz
-rw-r--r-- 1 timo timo 4300585 May 19 14:19 rest2-31786-log.log
-rw-r--r-- 1 timo timo 157327 May 19 14:19 rest2-31786-error.log
-rw-r--r-- 1 timo timo 4141677 May 19 14:19 rest2-31786-gc-log.log.3.current

那如何解决这个问题呢?

在nginx的upstream里有相应的共享zone的配置。

1
2
3
4
5
6
Syntax:	zone name [size];
Default: —
Context: upstream
This directive appeared in version 1.9.0.

Defines the name and size of the shared memory zone that keeps the group’s configuration and run-time state that are shared between worker processes. Several groups may share the same zone. In this case, it is enough to specify the size only once.

那我们就来测试一下看看。

结果是没有用的,只要worker进程重启之前,我服务变更过对应端口,那被nginx新拉起来的woker进行里的数据还是上次master进行reload的数据,相应worker之间的数据被没有被共享。这块内容还需要重新看下代码了。

这种对于后端是固定ip固定端口的是没有问题,但是对于微服务的是非常不合适的。

于是我们就自己写了一个监听服务,当nginx worker进程有重启的情况下,我们会往所有worker的内存重新写入一次。 这样就不会出现异常了。 当然这个监听服务也要用systemd来保持。