美文网首页
18ms问题深度分析

18ms问题深度分析

作者: 记忆挽留 | 来源:发表于2018-07-25 16:53 被阅读0次

    问题由来

    由于公司项目在美国一共有3个机房,其中2个机房之间的内网存在比较大的延迟约为18ms(正常内网延迟在0.1~0.5ms之间),由于服务之间会有往复通信,所以往往这18ms变成了n*18ms的延迟,带来的影响是秒级的延迟。

    以下是实际两个机房ping所得到的延迟:

    ping-64byte

    ping命令默认传输64byte数据,下面我们来验证下增加传输数据量所带来的影响

    ping-65507byte

    从测试数据来看数据包量从64byte扩大到65507byte,增加了1023倍,但是造成的影响只是1ms左右的延迟,可见数据包的大小对18ms问题带来的影响是很小的。


    下面我们分析下 http单次通信所造成的延迟

    ping原理

    ping指令实际上是应用层直接调用ip层的ICMP协议进行发送,且IP层协议是无连接的。ICMP协议的定义如下

    ICMP在OSGI中的位置,摘自《TCP/IP协议族》,第9章“网络控制报文协议(ICMP)”

    ping命令执行过程

    (1)Ping命令会构建一个固定格式的ICMP请求数据包,然后由ICMP协议将这个数据包连同地址“192.168.1.2”一起交给IP层协议ping能够计算往返时间RTT,它在报文的数据部分插入发送时间。

    (2)IP层协议将以地址“192.168.1.2”作为目的地址,本机IP地址作为源地址,加上一些其他的控制信息,构建一个IP数据包,并在一个映射表(ARP实现  IP地址转成Mac地址的协议)中查找出IP地址192.168.1.2所对应的MAC地址(这是数据链路层协议构建数据链路层的传输单元——帧所必需的),一并交给数据链路层。

    (3)数据链路层构建一个数据帧,目的地址是IP层传过来的MAC地址,源地址则是本机的物理地址,还要附加上一些控制信息,依据以太网的介质访问规则,将它们传送出去。    

    (4)主机B收到这个数据帧后,先检查它的目的地址,并和本机的物理地址对比,如符合,则接收;否则丢弃。接收后检查该数据帧,将IP数据包从帧中提取出来,交给本机的IP层协议。同样,IP层检查后,将有用的信息提取后交给ICMP协议,后者处理后,马上构建一个ICMP应答包,发送给主机A,其过程和主机A发送ICMP请求包到主机B一模一样。

    剥去可以忽略不计的硬件处理耗时,这18ms正是网络往返一次所花费的时间,故而单趟通信耗时应该为9ms左右。

    http状态下的耗时情况

    众所周知http是基于tcp/ip协议的,而tcp/ip意味着 TCP 和 IP 在一起协同工作,TCP 负责应用软件和网络软件之间的通信,IP 负责计算机之间的通信,TCP 负责将数据分割并装入 IP 包,然后在它们到达的时候重新组合它们,IP 负责将包发送至接受者。

    http为应用层协议,在tcp基础上又封装了3次握手,4次挥手,重发,拥塞控制等特性。

    tcp三次握手 tcp四次挥手

    故而整个http单次请求过程应该是9*3ms(3次握手)+9*2ms(http请求及应答)+9*4ms(4次挥手)=81ms,但真实情况是小于这个值的,下面我们一起来分析下原因。

    我们采用curl进行接口请求,并对响应时间进行统计,结果只花费了37ms

    time_namelookup: 0.000

    time_connect: 0.019

    time_appconnect: 0.000

    time_pretransfer: 0.019

    time_redirect: 0.000

    time_starttransfer: 0.037

    time_total: 0.037

    随后我们使用了tcpdump进行抓包

    tcp dump

    整体过程下面来简述下(后面时间统计是与上一步时间的差值)

    1.client    >    server    内容:SYN

    2.server    >    client    内容:SYN  ACK     耗时:18.646ms 

    413240-394594=18646,此时间含义是从1.client端发送SYN 到  2.sever端接收到

    SYN ACK 报文时总共花费了18.646ms,都是以client时间为基准,下面不做过多赘述

    3.client    >    server    内容:ACK    耗时:0.048ms 

    4.client    >    server    内容:http get请求    耗时:0.148ms

    解读:client端接收到了syn ack后很快就回复了ack,三次握手结束后也快速的发起了http request

    5.server    >    client    内容:ack    耗时:18.154ms

    解读:server端收到了client的request后返回ack,此处18ms问题凸显

    6.server    >    client    内容:http segment    耗时:0.105ms

    7.client    >    server    内容:ACK    耗时:0.026ms

    8.server    >    client    内容:http segment    耗时:0.032ms

    9.client    >    server    内容:ACK    耗时:0.007ms

    10.server    >    client    内容:response体结束    耗时:0.013ms

    11.client    >    server    内容:ack    耗时:0.003ms

    解读:从6~11位http发送request到拿到完整response的过程,在此过程中http严格按照一问一答的模式进行,只是server端似乎不关心client的ack,而是一股脑的在发送数据,为什么会这样呢?

    这里普及一个小知识点。http三次握手之后开始发送tcp segment(分段数据包),第一次能发送的没有被ack的segment数量是由initial tcp window大小决定的。这个initial tcp window根据平台的实现会有差异,但一般是2个segment或者是4k的大小(一个segment大概是1500个字节),也就是说当你发送的包大小超过这个值的时候,要等前面的包被ack之后才能发送后续的包。initial tcp window即为tcp默认发送窗口大小,当窗口左侧靠拢右侧闭合之前,都可以进行无ack发送。

    有关滑动窗口 https://www.cnblogs.com/freebird92/p/6442155.html   

    什么限制了tcp segment大小,这里需要提一下MSS以及MTU

    MTU(Maximum Transmission Unit,最大传输单元)

    MSS(Maximum Segment Size,最大报文长度)

    MSS=MTU-20字节TCP报头-20字节IP报头,那么在以太网环境下,MSS值一般就是1500-20-20=1460字节。此数值在http三次握手时被携带。

    mss约定时机

    12.client    >    server    内容:FIN    耗时:0.251ms

    13.server    >    client    内容:FIN    耗时:18.197ms

    14.client    >    server    内容:ACK    耗时:0.036ms

    解读:client端接收到了完整的response,在12步中主动发起了FIN进行4次挥手,18.197ms 后即13步时,client端收到了server端的FIN请求,客户端回发ACK,此时完整的http流程结束。

    为什么挥手只有3次?

    其实tcp协议不一定总是进行4次挥手,在client和sever端同时发送FIN时只有3次挥手。

    总结:curl实际上没有完整的统计握手到挥手结束整个过程的用时,1~11步耗时累计为37.182ms,与curl统计相吻合,也就是说curl统计的时间舍弃了4次挥手的用时。


    最后一张图总结一下

    其中http response分段包部分sever端并未等待client响应的ack,所以用虚线标识

    整个过程



    对于18ms的一些思考

    对于发送http而言,18ms问题主要在2个过程中显现

    第一个是三次握手的第二步client端收到sever端的syn ack时

    此处影响了链接的建立

    第二个是client端真正开始发送http请求到sever端发送响应数据时,此处影响的是response到达的时间。

    而由于滑动窗口的存在,18ms在http报文分段传输过程中的影响是较小的,因为对于sever端来说发送与client端的ack是分开的,只要窗口不闭合sever端总是能不断的发送数据。

    而挥手过程中的延迟,这就要具体情况具体分析了,如果程序需要等到完整的挥手结束才算结束运行,那么整个过程变成了18ms+37ms=55ms

    应对方案

    从技术角度出发,减少链接建立的次数,采用http链接池+keepalive机制,最大化复用链接

    从业务接口设计角度出发 ,减少client对sever端的请求次数,单次请求中携带更多的业务信息

    相关文章

      网友评论

          本文标题:18ms问题深度分析

          本文链接:https://www.haomeiwen.com/subject/yoypmftx.html