Nginx神奇的499竟然不在HTTP响应码标准内?快来了解一下!

news2025/5/24 18:20:55

1 前言

高性能的HTTP和反向代理服务器,Nginx用来:

  • 搭建Web Server
  • 作负载均衡
  • 供配置的日志字段丰富,从各类HTTP头部到内部性能数据都有

Nginx的访问日志中,存在499状态码的日志。但常见4xx状态码只有400、401、403、404等,499并未在HTTP RFC文档。这499错误日志,在流量较大场景下,特别是面向Internet的Web站点场景下还是很常见 。

2 案例

某客户反馈:Nginx服务器连续几天记录较多499错误日志,之后几天趋零,再回升,整体状况不定。

经营的To C产品,跟手机端App协同。App会定时把消息上传到微信消息网关,后者再把这些消息推送到该客户的服务端(在公有云上)做业务处理,整体消息量约每日三十万条。对消息网关来说,这服务端就是一个Web回调接口:

499日志趋势:

image-20230914091509298

由于大量499日志存在,客户非常担心业务已受影响,如终端消费者是否经常上传数据失败?499状态码本身意味着啥呢?查它在Nginx的 官方定义:

NGX_HTTP_CLIENT_CLOSED_REQUEST | 499

client closed request(客户端关闭了请求)?说了跟没说没区别。啥引起“客户端关闭请求”?

解决问题办法,可能不在问题自身所处层面

应用层日志记录的只是表象。更深层次原因可能在更底层,如传输层或网络层。搞清499:

  • 不仅是理解这个499码底层含义
  • 而且通过排查,掌握一套 对HTTP返回码进行网络分析的方法。对维护好Nginx以及其他Web服务都有助

来抓包分析HTTP返回码真正含义。

3 锚定到网络层

软件文档已无法查清根因,所以要下沉到网络层排查。如你处理应用层故障,如HTTP异常返回码(4xx和5xx系列),也遇到应用层找不到答案,就抓包分析。

后文

  • “客户端”指微信消息网关
  • “服务端”指这客户在公有云的服务器

服务端 使用tcpdump工具抓包,Wireshark打开抓包文件。从抓包文件中一般寻找可疑报文。这次抓包有不少RST报文,过滤出典型的带RST报文的TCP流:

抓包文件可关注【JavaEdge】联系本人领取。

结尾处RST。这TCP流一定跟499日志有关系吗?得益于TCP/IP精妙分层设计,应用层只需通过系统调用,就可像使用文件IO那样使用网络IO,具体的网络细节都由内核处理。可由此也带来问题: 应用层视角无法“看到”具体的网络报文。

需根据关键信息确定应用层日志跟网络报文的对应关系。如这里,可确认上面这带有RST的TCP流,就是日志中记录的一条499日志记录。因为:

  • 客户端IP:日志中的remote IP跟抓包文件里面的IP符合
  • 时间戳:日志的时间戳也跟这个TCP流的时间吻合
  • 应用层请求:日志里的HTTP URL路径和这个TCP流里的URL相同

04也是类似方式找到应用日志跟报文对应关系。

真实抓包分析场景中,“如何把应用层问题跟网络层抓包关联”,始终是关键环节。也是令人困扰的关键技术障碍。所以这里方法可参考,再处理这种关键环节,也可根据上面提到的三维即IP、时间戳、应用层请求(包括URL和header),把应用层问题锚定到网络层数据包。

既然确定这个流就代表一次499事件,好好分析报文。

4 解密TCP流

TCP流示意图红框部分是重点。

image-20230914101947340

报文1~3,表示TCP握手成功。

报文4(客户端发出),表示客户端向服务器发报文,报文里只包含HTTP header,其声明该请求为POST方法,但不含POST body。正常,因为HTTP协议规定数据先后顺序:先header(包含method、URL、headers),后body。所以,既然method和URL单独位于一个报文,按顺序body就在后续的报文。

报文5(服务端发出),确认报文:我(服务端)确认收到了你(客户端)发过来的报文4。

报文6(客户端发出),距上一报文2s。这报文被Wireshark标红,注释TCP Previous segment not captured:它之前的TCP报文段没被抓到。

之前的TCP报文”是啥?

就是按TCP序列号顺序,排在当前报文之前的报文。先关注右边红框FIN标志位,说明这是客户端主动关闭连接的报文。

目前报文情况:

image-20230914103706786

明明HTTP POST请求的body(也称HTTP载荷)还没发过来,客户端就要关闭连接?好比朋友说:“我有个事情要你帮忙,嗯,拜~”,你刚听到上半句他的求助意向,还没听到这忙是个啥,他就跟你说再见。可能暂时看不出问题,先放放。

报文7(服务端发出)。服务器收到FIN+ACK报文(6号报文),但发现序列号不是它期待的309,而是777,于是服务器TCP协议栈判断:有一个长度为777-309=468的TCP段(TCP segment)丢失了。

按TCP约定,这时服务端只可确认其收到的字节的最后位置,在这里就是上次(报文5)的ACK位置。形式上,报文7就成了一个DupAck(重复确认)。

当客户端收到DupAck时,它就要长个心眼:“情况有点微妙,如果凑满3个DupAck可能有丢包”。

如凑满3个DupAck就重传的机制,被称为快速重传机制,12深入学习过。

报文4的TCP信息:

按TCP设计,客户端将发送的下一个报文的序列号(309)= 本次序列号(1) + 本次数据长度(308),即Next sequence number。

报文8(客户端发出),16s之久,客户端 重传 了这个报文,包含POST body,长度 468 字节。就跟777-309=468对应。

明明这468字节的报文是第一次出现,怎么算重传?因为,这个抓包文件是在服务端生成,所以它的视角无法看到多次传送同样这个报文的现象。但我判断,客户端抓包,一定可看到这个468字节的报文被试图传送多次。

服务端视角判断,一开始这报文应该是走丢,没到达服务端,所以没在这服务端抓包文件里。又因为过16s才到,很可能不是单纯一次重传,而是多次重传后才到达。因此确实属重传。

报文9,服务端对这POST body的数据包回复确认报文。

报文10,服务端发HTTP 400的响应报文给消息网关。这信息并没有被Wireshark直接按HTTP格式进行展示,但因HTTP是文本编码,所以可鼠标选中Transmission Control Protcol部分,在底下文本栏直接看到HTTP 400这段文本:

这HTTP 400报文也带FIN标志位,即服务端操作系统“图省事”,把应用层的应答数据(HTTP 400),跟os对TCP连接关闭的控制报文(这个FIN),合并在同一个报文。03提到的搭顺风车(Piggybacking),提升网络利用率。

这阶段报文:

从这些报文顺序来看,确实有问题:

  • 服务端先收到HTTP header报文,没收到期望的HTTP body报文,而是收到FIN报文(客户端试图关闭连接)。HTTP请求还没发到服务端,服务端回复HTTP响应更无从谈起,客户端就发FIN不符常理(

  • 服务端回复HTTP 400,并发送FIN关闭这连接

  • 客户端回复RST彻底关闭这连接

    客户端先发送了FIN,之后才发POST body。 全部过程:

服务端还没回复数据而客户端已经要关闭连接,按499官方定义,这种行为被Nginx判定499状态:

  • 对内表现为记录499日志
  • 对外表现为回复HTTP 400给消息网关

所以,在服务端的Nginx大量499日志条目;在消息网关那头,如果它也做Web日志,就是400报错。

5 从现象到本质

为什么客户端先发送FIN,然后才发送POST body?

回到Wireshark窗口,再关注6号报文:

离上个报文相差了2s。

往前看4号报文:

离3号报文相差3s。加起来,6号报文离TCP握手完成,正好隔 5s整

一般出现这种整数,就越可疑,因为如果是系统或网络错乱导致,时间分布应 随机,不可能卡在整数时间。经验看, 这和人为设置有关。于是客户仔细查看微信网关使用文档,发现5s超时设置。即若一个HTTP事务无法在5s内完成,就关闭这连接。

啥叫无法完成?

在这抓包里即:HTTP header报文发过去了,但HTTP body报文没一起过去(网络原因导致)。而由于初始阶段报文少, 无法凑齐3个DupAck,所以快速重传没有启动,只好依赖超时重传(12 讲),且这多次超时重传也失败,服务端只好持续等待这丢失的报文。5s后,客户端没收到服务端响应,就主动关闭这次连接(可以下次再试,这次就不继续干等)。

即该场景里Nginx 499错误日志主因:

  • 消息网关—>服务器 方向上的一个TCP包丢失(案例里是HTTP POST body报文),引起服务端空闲等待
  • 消息网关有5s超时设置,即连接达到5s,消息网关就发FIN关闭连接

逻辑链:

  • 要解决499报错的问题,就需要解决5s超时
  • 要解决5s超时,就要解决丢包
  • 要解决丢包,就要改善网络链路质量

最根本解决方案,如何确保客户端到服务端的 网络连接 可靠稳定,使类似的报文延迟现象降到最低。只要不丢包不延迟,HTTP事务就能在5s内完成,消息网关就不会启动5s超时断开连接机制。

跟客户还有网关工程师配合,确实发现网关到公有云的一条链路有问题。更换为另外一条链路后,丢包率大幅降低,问题极大改善。虽然还是有极小比例的错误日志(约万分之一),但对客户已在可接受范围。

因为丢包,客户端FIN报文跟HTTP POST body报文一样,也可能丢失。不过,无论这FIN是否被服务端及时收到,这次HTTP事务本身也已在客户端记为失败。

链路丢包这种问题挺明显,为啥没及时发现?

  • 虽然对主要链路的整体状况有细致的监控,但这里的网关到客户的公有云服务属于“点到点”的链接,本身也属于客户自身的业务,公有云难以对这种情况做监控,理想情况是客户自己来实现监控。
  • 客户的消息量很大,哪怕整体失败比例不高,但乘以绝对的消息量,产生的错误的绝对数也就比较可观了。

至于Nginx为何“创造”499状态码, Nginx源码 注释写得清楚。并非标新立异,而确实是为了弥补标准HTTP协议不足:

/*
 * HTTP does not define the code for the case when a client closed
 * the connection while we are processing its request so we introduce
 * own code to log such situation when a client has closed the connection
 * before we even try to send the HTTP header to it
 */
#define NGX_HTTP_CLIENT_CLOSED_REQUEST     499

6 总结

Nginx 499是Nginx定义状态码,不是RFC中定义HTTP状态码。表示“Nginx收到完整的HTTP request前(或者已经接收到完整的request但还没来得及发送HTTP response前),客户端试图关闭TCP连接”这种反常情况。

超时时间跟499报错数量也有直接关系。如果我们有办法延长消息网关的超时时间,比如从5秒改为50秒,那么客户端就有比较充足的时间去等待丢失的报文被成功重传,从而在50秒内完成HTTP事务,499日志也会少很多。

关注网络延迟对通信的影响。比如客户端发出的两个报文(报文3和报文4)间隔了3秒钟,这在网络通信中是个非常大的延迟。而造成这么大延迟的原因,会有两种可能:

  • 消息网关端本身是在握手后隔了3秒才发送了这个报文,属 应用层问题
  • 消息网关在握手后立刻发送了这个报文,但在公网上丢失了,微信消息网关就根据“超时重传”的机制重新发了这个报文,并于3秒后到达。属 网络链路问题

由于上面的抓包是在服务端做的,所以未到达服务器的包自然也不可能抓到,也就是无法确定是具体哪一种原因(客户端应用层问题或网络链路问题)导致,但这并不影响结论。

公网丢包现象不可能完全消失。千分之一左右公网丢包率是正常范围。由于:

  • 客户发送量较大(主因)
  • 微信消息网关设置5s超时相对较短(次要原因)

问题就会在这个案例中被集中暴露。

设置更长超时阈值(如50s)能解决?出错率会降低不少,但新问题:

  • 消息网关会有更多的资源消耗(内存、TCP源端口、计算能力等)
  • 消息网关处理事务的平均耗时会增加

所以,5s是权衡后的合适方案。

排查的方法论,对更广泛的应用层报错日志的排查,推荐:

  • 先查应用文档,初步确定问题性质,大体确定排查方向

  • 对比应用日志和抓取的报文,在传输层和网络层寻找可疑报文。这步,可采用比对策略找到可疑报文:

    • 日志中的IP跟报文中的IP对应
    • 日志和报文的时间戳对应
    • 应用层请求信息和报文信息对应
  • 结合协议规范和报文现象,推导出根因

FAQ

  • 第7个报文是DupAck,为什么没触发快速重传?
  • 消息网关那头的应用日志应该不是499,那会是啥样日志?

    本文由博客一文多发平台 OpenWrite 发布!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1010642.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【数据结构】二叉树的链式结构

【数据结构】二叉树的链式存储结构 二叉树的存储结构 typedef int BTDataType; // 二叉树的结构 typedef struct BinaryTreeNode {BTDataType data; // 树的值struct BinaryTreeNode *left; // 左孩子struct BinaryTreeNode *right;// 右孩子 } BinaryTreeNode;二…

【数据结构】TOP-K问题/使用堆解决

💐 🌸 🌷 🍀 🌹 🌻 🌺 🍁 🍃 🍂 🌿 🍄🍝 🍛 🍤 📃个人主页 :阿然成长日记 …

第70步 时间序列建模实战:ARIMA建模(JMP)

基于WIN10的64位系统演示 一、写在前面 这一期,我们使用JMP进行SARIMA模型的构建。 同样,这里使用这个数据: 《PLoS One》2015年一篇题目为《Comparison of Two Hybrid Models for Forecasting the Incidence of Hemorrhagic Fever with …

flutter简单的本地草稿箱功能

需求1:发帖退出时提示是否保存草稿 需求2:每条草稿中可以保存多张图片(最多9张)或一条视频及三十来个其它参数 需求3:每条草稿都是可以被覆盖的、可以点击删除 需求4:草稿页面可以一键清空 需求5:草稿随app删除一起没掉…

简单2D几何求交点

2D图形SVG工具添加了通过选项属性显示交点的功能。 PaladinDu:2D图形SVG化工具0 赞同 0 评论文章 在这个工具中,已经定义的数据结构有点,线段,有向线段,射线,直线,多边形,和圆。 …

【技巧】如何保护PDF文件不被随意修改?

做好的PDF文件,不想被他人随意修改,只要给PDF设置“限制保护”就可以了,设置保护后需要输入密码才能进行编辑。 设置“限制保护”我们需要用到PDF编辑器,以小编使用的编辑器为例,首先通过编辑器打开PDF文件后&#xf…

医学访问学者申请四点规划建议

医学领域一直以来都是人类社会的重要组成部分,而作为一名有志于成为一名医学领域的访问学者,您需要明确自己的目标并做好充分准备。知识人网小编将为您提供四点规划建议,以帮助您成功申请医学访问学者的机会。 第一点:明确研究方向…

input 的 placeholder 样式

::placeholder 伪元素 这个伪元素可以改变 input、textarea 占位文本的样式。 input::placeholder {color: green; }完整的兼容性写法: input {&::-webkit-input-placeholder, /* WebKit browsers*/ &:-moz-input-placeholder, /* Mozilla Firefox 4 to …

2个小时的腾讯面试经历(C++),来看看它终究考察了些什么?

今天分享一位同学实习面试鹅厂 c 岗的面试,全程都是问 C 和计算机基础(系统、网络、算法)的内容了。难度中规中矩吧,基本都是追问式的问法,一层一层往下问。 C相关 对面向对象的理解 C面向对象编程就是把一切事物都…

Kubernetes 部署应用(nginx)的两种方式,你更喜欢哪一种?

k8s发布应用的两种方式: kubernetes-dashboardkubectl命令行 一、Dashboard方式 配置部署:包含应用名称、容器镒、pod数量、Service非常的方便,不想设置配置yaml的可以很方便的部署。 点击部署就成功了k8s应用的部署了。部署后可以看到相应…

python爬虫经典案例(一)

爬虫(Web Scraping)是一种自动获取互联网信息的技术,广泛用于数据采集、分析和应用开发。无论你是数据科学家、市场营销专家还是应用程序开发者,都可以通过编写爬虫来获取所需的信息。在本文中,我们将介绍五个实用的爬…

从0开始实现简易版vue2

文章目录 前言原理思路分析实现Observer实现Watcher实现Compile 前言 Vue.js的双向绑定原理是其最重要的特性之一,它使得数据模型和DOM之间的同步变得非常简单和高效。 先上个成果图来吸引各位: new SimpleVue({el: #app,data: {title: SimpleVue,name…

re学习(37)DASCTF 2023_7 controlflow

IDA载入一下 发现flag的长度为40,然后将input[i]的值赋值给str[i],str[i]又执行一个异或操作,但是上面又有一些函数,导致这个程序的控制流有点奇怪..... 输入40只,检验是否是flag。 F5直接罢工了,暂时先忽…

Java入坑之代理

一、代理模式和静态代理 1.1代理模式 代理模式 - Proxy Pattern, 23个经典模式的一种,又称委托模式 -为目标对象提供(包装)了一个代理,这个代理可以控制对目标对象的访问 外界不用直接访问目标对象,而是访问代理对象,由代理对象再…

软件外包开发bug管理工具

国内有一些常用的Bug管理工具,这些工具旨在帮助团队有效地发现、跟踪和解决软件开发中的缺陷和问题。以下是一些国内的Bug管理工具,希望对大家有所帮助。北京木奇移动技术有限公司,专业的软件外包开发公司,欢迎交流合作。 1.禅道&…

在openSUSE上开启护眼模式

色温 色温是用来衡量光源色彩时所用到的一个概念,单位为开尔文。热黑体辐射体与光源温度相同时的温度,就是该光源的色温。 显而易见,色温越低时,光源看起来越黄;色温越高时,光源看起来越蓝。下面是一些常见…

【LangChain系列 9】Prompt模版——MessagePromptTemplate

原文地址:【LangChain系列 9】Prompt模版——MessagePromptTemplate 本文速读: MessagePromptTemplate MessagesPlaceholder 在对话模型(chat model) 中, prompt主要是封装在Message中,LangChain提供了一些MessagePromptTemplat…

入门必读:Python try except异常处理详解

Python 中,用try except语句块捕获并处理异常,其基本语法结构如下所示: try: 可能产生异常的代码块 except [ (Error1, Error2, ... ) [as e] ]: 处理异常的代码块1 except [ (Error3, Error4, ... ) [as e] ]: 处理异常的代码块…

CSAPP Bomb Lab

CSAPP Bomb Lab 芜湖,完成实验来记录一下啦~ 这个lab看的我真是眼皮发麻,框框就是反汇编->c语言形式->优化c语言 最后我才能写出来,整的我睡觉都是 mov… lea… 难道适中,感觉只要耐心一点就没问题,好了现在开…

【鸿蒙(HarmonyOS)】List列表、ArkUI资源组数据类型

一、引言 描述:List列表在移动端设备中最为常见。比如:通讯录、短信、聊天软件等都,都拥有他的身影。难度:简单知识点:1、列表组件的使用2、认识ArkUI资源组数据类型 二、列表List 1、发现问题(Bug&…