概述:
在nginx做一些请求内容过滤的操作。当用户上传一个大文件时,偶然会有上传失败的情况,当关闭内容过滤时问题不再发生。
初步定位是和文件过滤有关系,在测试环境模拟了很多次都无法复现。
问题追踪
- 在生产环境抓包,上传失败是因为nginx发送rst包reset和客户端的连接。
首先reset肯定不是一个正常的关闭连接。先排查是否nginx在某种情况下会发送reset。
查看了nginx代码,发现只要配置了reset_timedout_connection
指令就会有reset连接的可能,看线上配置也没有配置该指令。
- 继续查看抓包文件,发现nginx接收窗口先满,大约60s内客户端一直再发送零窗口探测。然后nginx才发送reset。
零窗口探测难道有超时设置,查看《tcp/ip协议详解》几乎说所有的实现都没有超时,零窗口会一直探测。
- 又仔细看了一下抓包文件,发现nginx接收窗口先满,大约60s内客户端一直再发送零窗口探测,然后源站发送fin包关闭了和nginx的连接,接着才是nginx reset了客户端的连接。
猜测是源站读请求内容超时,导致源站关闭连接,发送fin包。
分析了nginx核心循环的代码,nginx在主循环里优先处理网络事件,因此可能是包过滤调用了hypherscan导致nginx进程陷入。
而源站关闭连接后,nginx也要关闭和客户端的连接,而此时接收窗口还有数据没有接收。网络层就发送rst包reset掉和客户端的连接了。 以下python代码可以复现reset包。
# -*- coding: utf-8 -*-
import socket
import sys
import time
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_address = ('127.0.0.1', 10001)
sock.bind(server_address)
sock.listen(1)
while True:
connection, client_address = sock.accept()
time.sleep(1)
connection.recv(1)
connection.close()
print("close")
根据业务逻辑,修改了代码减少了包过滤的内容,上线观察了一段时间后问题解决。
评论前必须登录!
注册