现象:nginx在加入nginx_upstream_check_module健康检查模块后error.log中经常出现recv() failed (104: Connection reset by peer)错误
1 | 2378:2017/09/12 14:57:40 [error] 34722#0: recv() failed (104: Connection reset by peer) |
该模块中有该错误产生:
1 | [error] 25592#0: enable check peer: 47.90.36.208:443 |
(错误含义:对该ip启动健康检查),属于该模块的正常日志报错,在满足某种条件下才会产生(服务器初始为down,在该模块检查超过成功检查配置次数后,标记该服务器为up),
类似的还有:
1 | [error] 41689#0: disable check peer: 47.90.36.208:443 (服务器初始为up,一般在该模块检查超过失败检查配置次数后,标记该服务器为down) |
1 | Breakpoint 1, ngx_unix_recv (c=0x7fffd00874c0, buf=0x7fffffffd210 "\220\006", size=4096) at src/os/unix/ngx_recv.c:136 |
该函数读取tcp链接对端发送过来的数据 这里n返回-1,根据该函数的解释: These calls return the number of bytes received, or -1 if an error occurred. 表示读数据时发生一个错误。
在nginx中,当err不为 NGX_EAGAIN ,NGX_EINTR 这两种状态则认为出错,并打印出错误码,该错误码为linux系统错误码。
1 | if (err == NGX_EAGAIN || err == NGX_EINTR) { //这两种情况 ,需要继续读 |
1 | 0x00007ffff7bcb813 in recv () from /lib/x86_64-linux-gnu/libpthread.so.0 |
发现的确有ngx_http_upstream_check模块对ngx_unix_recv的调用, 查看ngx_http_upstream_check_discard_handler 该函数在后台健康检查的作用: 该函数在链接关闭前不断从链接中读取数据,读取的数据不做任何处理,相当于丢弃。 而104错误就是在这不断读取中造成的。
1 | Breakpoint 1, ngx_http_upstream_check_discard_handler (event=0x7fffcf65e218) at /home/gjf/cloudfence/bdwaf/nginx_upstream_check_module/ngx_http_upstream_check_module.c:1233 |
源码:
1 | ngx_http_upstream_check_discard_handler(ngx_event_t *event) |
接着查看是否跟后台服务器有关,发现只有个别服务器会产生104错误。经测试,在删除该服务器所对应网站在nginx的代理配置后,104的错误不在产生
1 |
|
1 | 2017/09/12 11:48:23 [error] 31222#0: enable check peer: 218.17.254.67:80 |
由此猜想可能与后台服务器处理该链接健康检查模块的链接过程有关, 经查阅相关资料有关这样的描述:
errno = 104错误表明你在对一个对端socket已经关闭的的连接调用write或send方法,在这种情况下,调用write或send方法后, 对端socket便会向本端socket发送一个RESET信号,在此之后如果继续执行write或send操作,就会得到errno为104,错误描述为connection reset by peer。
5 接着采用tcpdump对该服务进行抓包:
1 | sudo tcpdump -i eth0 -s 0 -w test.pcap host 218.17.254.67 |
经多次抓包观察发现,发现该服务器经常会向109发送reset报文。
reset.png
reset报文发送场景(可能还有更多):
1 | 1 当尝试和未开放的服务器端口建立tcp连接时,服务器tcp将会直接向客户端发送reset报文 |
结论:
1 | 1 由此可基本确定为产生104该错误的原因为,对该模块发出的tcp方式的健康检查包,对端服务器的处理(关闭了该链接)。 |
参考链接: