Skip to content

HTTP chunked编码异常导致业务下载附件慢故障案例

故障现象

某业务应用系统基于B/S架构开发,其应用页面打开正常,但是该应用在下载附件时,速度非常慢。

故障分析

在下载附件很慢时,在服务器上捕获客户端与服务器业务交互的报文。其TCP会话交互的情况如下图所示:

点击查看原图

我们首先分析客户端与服务器三次握手建立连接的情况,查看客户端、服务器协商的MSS值是否正常,我们首先查看客户端SYN报文,其TCP MSS协商为1460,如下图所示:

点击查看原图

接下来查看服务器SYN/ACK报文,其TCP MSS也为1460,如下图所示:

点击查看原图

这说明双方协商的MSS值不存在问题,另服务器对客户端的get请求的第一个应用响应报文长度为248B,如下图所示:

点击查看原图

这也充分说明业务服务器在给客户端发送报文时并不是一直只能发送9字节的报文。那么是什么原因导致业务服务器突然之间只能每次发送9字节的应用层数据呢?

我们注意到服务器给客户端的第一个HTTP应用响应报文,如下图所示:

点击查看原图

其Transfer-Enconding是chunked

HTTP为什么会使用chunked传输编码?我直接引用《HTTP1.1中CHUNKED编码解析》一文中的描述来说明:一般HTTP通信时,会使用Content-Length头信息性来通知用户代理(通常意义上是浏览器)服务器发送的文档内容长度,该头信息定义于HTTP1.0协议RFC 1945 10.4章节中。浏览器接收到此头信息后,接受完Content-Length中定义的长度字节后开始解析页面,但如果服务端有部分数据延迟发送吗,则会出现浏览器白屏,造成比较糟糕的用户体验。
解决方案是在HTTP1.1协议中,RFC 2616中14.41章节中定义的Transfer-Encoding: chunked的头信息,chunked编码定义在3.6.1中,所有HTTP1.1 应用都支持此使用trunked编码动态的提供body内容的长度的方式。进行Chunked编码传输的HTTP数据要在消息头部设置:Transfer-Encoding: chunked表示Content Body将用chunked编码传输内容。根据定义,浏览器不需要等到内容字节全部下载完成,只要接收到一个chunked块就可解析页面.并且可以下载html中定义的页面内容,包括js,css,image等。”

Chunked编码一般使用若干个chunk串连而成,最后由一个标明长度为0的chunk标示结束。每个chunk分为头部和正文两部分,头部内容指定下一段正文的字符总数(非零开头的十六进制的数字)和数量单位(一般不写,表示字节).正文部分就是指定长度的实际内容,两部分之间用回车换行(CRLF)隔开。在最后一个长度为0的chunk中的内容是称为footer的内容,是一些附加的Header信息(通常可以直接忽略)。”

《HTTP1.1中CHUNKED编码解析》一文中使用了下图作为chunked解码的示意:

点击查看原图

通过这张示意图,我们可以清晰的了解到,chunked解码主要由2块组成,一块为指定数据块长度,以\ \ 回车换行作为结束符,另一块为实际的应用数据,也以\ \ 回车换行作为结束符。
知道了chunked解码的组成,我们再来看看业务服务器给客户端发送的应用层长度仅9字节的报文解码:

点击查看原图

根据这9个字节的应用字段解码,我们将其十六进制换算为正常的十进制,如下表所示:

点击查看原图

我们根据上图即可解码服务器发往客户端的chunked编码报文,其意义为:
1,指明长度为0001,即为1字节;
2,该报文真正的应用数据仅为1字节的“R”!

服务器每次仅传输一个字节给客户端!其报文的有效载荷率仅为: 1/63=0.015873015873015873015873015873016!
如果客户端下载一个1MB大小的附件,服务器端将产生1*1024*1024=1048576个报文,如果客户端每个报文都确认的话,一个下载将产生1048576次交互,即使往返时间RTT值非常小,再乘以1048576的基数之后,也将是个难以接收的数值。
如此,我们终于明白下载附件慢的原因了。

分析结论

此为业务服务器HTTP chunked编码传输时,自己指定的数据块长度值太小(为1字节)导致的故障。

参考文档

1,《HTTP1.1中CHUNKED编码解析》
2,RFC 2616《超文本传输协议》

评论与留言

欢迎留言。你可以匿名留言,也可以自愿留下网名或邮箱;邮箱不会公开展示。

还没有留言,欢迎交流。

发表留言

网络分析技术档案