超高性能管线式HTTP请求(实践·原理·实现)
由于发送速度过快直到发出一大半近70个request的时候第一个tcp确认包序号为353的包(只是确认包不是response)才发出(327的ack),而且服务器很快就发现下一个包出问题了并引发了TCP DUP ACK (https://ask.wireshark.org/questions/29216/why-are-duplicate-tcp-acks-being-seen-in-wireshark-capture 产生原因可以参考这里)。 【TCP DUP ACK 出现在接收方发现数据包缺口时(数据包失序),这种情况就会发送重复的ACK,这不仅用于快重传,会触发比快重传更快的恢复机制(Fast Retransmission)如果发现重复的ACK,但是报文中未发现缺口,这表示你捕获的是数据来源(而不是接收方),这是十分正常的如果数据在发往接收方的时候发生了丢失。你应该会看到一个重传包】。 其实就是说服务器没有发现下一个包后面又发了3次(一共4次·)TCP DUP ACK 都是针对353的,所以后面客户端很快就重传了TCP DUP ACK 所指定的丢失的包(即下面看到的362) 后面还可以看到由于过快的速度,还造成了部分的失序列(out of order)。不过需要说明的是,这些错误在tcp的传输中是很常见的,tcp有自己的一套高效的机制对这些错误进行恢复,即便有这些错误的存在也不会对pipe的实际性能造成影响。 如果服务器异常误包不能马上被恢复可能会造成指数退避的情况如下图: 高速收发带来的问题,不仅有丢包,失序,重传,无论是客户端还是服务器都会有接收窗口耗尽的情况,,如果接收端窗口耗尽会出现TCP ZeroWIndow / Window full。 所以无论是客户端还是服务器都需要快速读取tcp缓冲区数据。 通过对TCP流的检查可以确定在本次测试中的部分管道的100条request是全部发出后,response才逐步被服务器发出。 现在看一下response的回复情况 因为response本身很大,而客户端的MSS只有1460 (上面看到的1506不是超过了MSS的意思,实际该数据包只有1424,加上48个字节的TCP包头,20字节的ip包头,14字节的以太网包头一共是1506,正常tcp包头为20字节因为这个tcp包被拆包了,所以包头里多了28个字节的options)所以一个response被拆成了多个包。 通过报文不难看出这个response在网络中传输大概花了1ms不到的时间(大概730微秒),因为看到是过滤掉过端口(指定管道)的流量,实际上在这不到1ms的时间里另外的管道也是可能同时在接收数据的。 pipe之所以能比常规请求方式性能高出这么多,主要有以下几点: 1:管线式发送,每条request不要等response回复即可直接发送下一个(重点不在于使用的是同一条线路,而且不约等待回复) 2:多条请求打包发送,在网络条件合适的情况下一个包可以包含多条request 3:只要服务器允许只需要创建极少tcp链接 (因为非局域网的TCP线路一般都遵循慢启动,网络正常情况下需要一定时间后效率才能达到最高) 现在我们可以来说下pipe弊端 实际pipe早就被http1.1所支持,并且大部分nginx服务器也支持并开启了这一功能。 相比普通的http keepalive传输 pipe http 解决了HOL blocking (Head-of-Line Blocking),而正是不再遵循一发一收的模式,使得应用层不能直接将每个请求与回复一一对应起来,对部分需要提交并区分返回结果的POST一类的请求,这种方式显的不是很友好。 解决方法其实也很简单,在应用服务上为request于response加上唯一标签即可以区分,或者直接使用HTTP2.0(https://tools.ietf.org/pdf/rfc7540.pdf)(这也是2.0的一个重要改进,http2.0也是通过类似的方式为其每个帧添加标识当前stream的id来实现区分的)。 下面是pipe与常规http的简单对比 实现 如下为pipe的.NET简单实现类库,及应用该类库的deom 测试工具 实现过程还是比较简单的可直接参看GitHub工程,MyPipeHttpHelper为实现pipe的工具类(代码中有较详细的注释),PipeHttpRuner为使用该工具类编写的测试工具。 【编辑推荐】
点赞 0 (编辑:晋中站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |