欢迎光临
我们一直在努力

因业务逻辑太重导致nginx延迟读引发的问题

概述:

在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")

根据业务逻辑,修改了代码减少了包过滤的内容,上线观察了一段时间后问题解决。

总结

赞(0) 打赏

评论 抢沙发

评论前必须登录!

 

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏