设为首页 加入收藏

TOP

Nginx神奇的499竟然不在HTTP响应码标准内?快来了解一下!(一)
2023-09-23 15:44:30 】 浏览:112
Tags:Nginx 499 HTTP 应码标 解一下

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。 全部过程:

服务端还没回复数据而客户端已经要关闭连接,按49

首页 上一页 1 2 下一页 尾页 1/2/2
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇Java有关队列的基本操作 下一篇Spring Cloud 轻松解决跨域,别再..

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目