「networking latency」-

其他原因导致的网络延迟:
1)网络传输慢,导致延迟;
2)Linux 内核协议栈报文处理慢,导致延迟;
3)应用程序数据处理慢,导致延迟等等。

网络延迟

1)这个时间可能是单向的,指从源地址发送到目的地址的单程时间;
2)也可能是双向的,即从源地址发送到目的地址,然后又从目的地址发回响应,这个往返全程所用的时间

通常,我们更常用的是双向的往返通信延迟,比如 ping 测试的结果,就是往返延时 RTT(Round-Trip Time)。

应用程序延迟

从应用程序接收到请求,再到发回响应,全程所用的时间。也指的是往返延迟,是网络数据传输时间 加上 数据处理时间 的和。

获取网络延迟

可以使用 ping traceroute hping3 来获取网络延迟:

hping3

# hping3 -c 3 -S -p 80 baidu.com
HPING baidu.com (eth0 123.125.115.110): S set, 40 headers + 0 data bytes
len=46 ip=123.125.115.110 ttl=51 id=47908 sport=80 flags=SA seq=0 win=8192 rtt=20.9 ms
len=46 ip=123.125.115.110 ttl=51 id=6788  sport=80 flags=SA seq=1 win=8192 rtt=20.9 ms
len=46 ip=123.125.115.110 ttl=51 id=37699 sport=80 flags=SA seq=2 win=8192 rtt=20.9 ms

--- baidu.com hping statistic ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 20.9/20.9/20.9 ms

// -c 表示发送3次请求,-S 表示设置 TCP SYN,-p 表示端口号为 80

// 从 hping3 的结果中,你可以看到,往返延迟 RTT 为 20.9ms

traceroute

# traceroute --tcp -p 80 -n baidu.com
traceroute to baidu.com (123.125.115.110), 30 hops max, 60 byte packets
 1  * * *
 2  * * *
 3  * * *
 4  * * *
 5  * * *
 6  * * *
 7  * * *
 8  * * *
 9  * * *
10  * * *
11  * * *
12  * * *
13  * * *
14  123.125.115.110  20.684 ms *  20.798 ms

// --tcp表示使用TCP协议,-p表示端口号,-n表示不对结果中的IP地址执行反向域名解析

第一步、创建负载环境

# docker run --network=host --name=nginx-good -itd nginx
# docker run --network=host --name=nginx-latency -itd feisky/nginx:latency

// 测试 Nginx 服务已经启动

# curl http://10.10.50.199
# curl http://10.10.50.199:8080

// 测试两个主机的延迟

# hping3 -c 3 -S -p 80 10.10.50.199
...
len=44 ip=10.10.50.199 ttl=64 DF id=0 sport=80 flags=SA seq=2 win=29200 rtt=7.6 ms
...

# hping3 -c 3 -S -p 8080 10.10.50.199
...
len=44 ip=10.10.50.199 ttl=64 DF id=0 sport=8080 flags=SA seq=1 win=29200 rtt=7.6 ms
...

第二步、测试 Nginx 性能

对于 80 端口的 Nginx 服务:

# wrk --latency -c 100 -t 2 --timeout 2 http://10.10.50.199/
Running 10s test @ http://10.10.50.199/
  2 threads and 100 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     9.19ms   12.32ms 319.61ms   97.80%
    Req/Sec     6.20k   426.80     8.25k    85.50%
  Latency Distribution
     50%    7.78ms
     75%    8.22ms
     90%    9.14ms
     99%   50.53ms
  123558 requests in 10.01s, 100.15MB read
Requests/sec:  12340.91
Transfer/sec:     10.00MB

对于 8080 端口的 Nginx 服务:

# wrk --latency -c 100 -t 2 --timeout 2 http://10.10.50.199:8080/
Running 10s test @ http://10.10.50.199:8080/
  2 threads and 100 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency    43.60ms    6.41ms  56.58ms   97.06%
    Req/Sec     1.15k   120.29     1.92k    88.50%
  Latency Distribution
     50%   44.02ms
     75%   44.33ms
     90%   47.62ms
     99%   48.88ms
  22853 requests in 10.01s, 18.55MB read
Requests/sec:   2283.31
Transfer/sec:      1.85MB

很明显,8080 端口的 Nginx 性能弱于 80 端口的 Nginx 服务。

第三步、问题排查,分析流量

// 在远程主机中运行抓包命令

# tcpdump -i "ens160" -nn tcp port 8080 -w nginx.pcap

// 在本地主机执行性能测试

# wrk --latency -c 100 -t 2 --timeout 2 http://10.10.50.5:8080/

把抓取到的 nginx.pcap 复制到 Wireshark 进行分析:
1)选中某个 HTTP 数据包,右键,Follow TCP Stream,这样显示某个流中的数据包,而不是所有连接的数据包
2)打开流图,更直观查看数据包:Statics => Flow Graph => Displayed packets / TCP Flows / Standard source …

客户端的延迟确认

看到 40ms 这个值,我们可以猜测这是 TCP 延迟确认(Delayed ACK)导致的,因此 40ms 是最小超时时间。

我们要检查客户端是否开启延迟确认,就要看客户端开启哪些连接选项:

# strace -f wrk --latency -c 100 -t 2 --timeout 2 http://192.168.0.30:8080/
...
setsockopt(52, SOL_TCP, TCP_NODELAY, [1], 4) = 0
...

只有开启 TCP_QUICKACK 选项才会禁用 延迟确认。也就是说这里确实启用延迟确认。

服务端的 Nagle 算法

虽然客户端会延迟确认,但是并不会影响服务端发送数据包,也就是说响应不会很久。但是从图中可以看出,在客户端发送 ACK 之后,服务端才发送其他数据。

数据包 1720 与 1265 的 ACK 是相同的,他们是在 1719 之后才发送的。此时我们可以猜想服务端开启 Nagle 算法(纳格算法)

但是在延迟确认的基础上,二者配合会导致延迟明显:客户端的 ACK 在等待”顺风车“,而服务端收不到 ACK,由于 Nagle 算法会继续等待,这样服务端也无法响应。

检查 服务端的 Nginx 是否开启 tcp_nodelay 选项,将其设置为 on 即可解决问题。

参考文献

40 | 案例篇:网络请求延迟变大了,我该怎么办?