浅谈网络延迟

网络延迟

        网络延迟(Network Latency),人们通常认为它是指网络数据传输所需的时间。

        但是,这里的“时间”是指双向流量,即数据从源发送到目的地,然后从目的地地址返回响应的往返时间:RTT(Round-Trip Time)。

        除了网络延迟之外,另一个常用的指标是应用延迟(Application Latency),它是指应用接收请求并返回响应所需的时间。

        通常,应用延迟也称为往返延迟,它是网络数据传输时间加上数据处理时间的总和。

        通常人们使用ping命令来测试网络延迟,ping是基于ICMP协议的,它通过计算ICMP发出的响应报文和ICMP发出的请求报文之间的时间差来获得往返延迟时间。这个过程不需要特殊的认证,从而经常被很多网络攻击所利用,如,端口扫描工具nmap、分组工具hping3等。

        因此,为了避免这些问题,很多网络服务都会禁用ICMP,这使得我们无法使用ping来测试网络服务的可用性和往返延迟。在这种情况下,您可以使用traceroute或hping3的TCP和UDP模式来获取网络延迟。

例如:

# -c: 3 requests
# -S: Set TCP SYN
# -p: Set port to 80
# hping3 -c 3 -S -p 80 baidu.com
HPING baidu.com (eth0 39.156.66.10): S set, 40 headers + 0 data bytes
len=40 ip=39.156.66.10 ttl=51 id=8101 sport=80 flags=SA seq=0 win=8192 rtt=39.9 ms
len=40 ip=39.156.66.10 ttl=51 id=48516 sport=80 flags=SA seq=1 win=8192 rtt=39.8 ms
len=40 ip=39.156.66.10 ttl=51 id=18386 sport=80 flags=SA seq=2 win=8192 rtt=49.7 ms

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

当然,你也可以使用traceroute:

# traceroute --tcp -p 80 -n baidu.com
traceroute to baidu.com (39.156.66.10), 30 hops max, 60 byte packets
1  172.18.1.1  2.665 ms  3.893 ms  5.950 ms
...
14  * * 39.156.66.10  28.805 ms

        traceroute会在路由的每一跳(hop)发送三个数据包,并在收到响应后输出往返延迟。如果没有响应或响应超时(默认 5s),将输出一个星号*。

案例展示

我们需要在此演示中托管host1和host2两个主机:

  • host1 (192.168.47.128):托管两个Nginx Web应用程序(正常和延迟)

  • host2 (192.168.47.129):分析主机

host1 准备

在host1上,让我们运行启动两个容器,它们分别是官方Nginx和具有延迟版本的Nginx:

# 正常的nginx
# docker run --network=host --name=good -itd nginx
Unable to find image 'nginx:latest' locally
latest: Pulling from library/nginx
9e3ea8720c6d: Pull complete 
bf36b6466679: Pull complete 
15a97cf85bb8: Pull complete 
9c2d6be5a61d: Pull complete 
6b7e4a5c7c7a: Pull complete 
8db4caa19df8: Pull complete 
Digest: sha256:480868e8c8c797794257e2abd88d0f9a8809b2fe956cbfbc05dcc0bca1f7cd43
Status: Downloaded newer image for nginx:latest
047094f369a331b6e7b3a72f43e47695c9dae41e00addf85861f3080b9e9161e
# 延迟的nginx
# docker run --network=host --name=good -itd nginx
58f015e037bfb5a88139ce3b29468430f1ec5c61e2f29118291fa1cf7cfd0f28
root@ubuntu:~# docker run --network=host --name=latency -itd feisky/nginx:latency
Unable to find image 'feisky/nginx:latency' locally
latency: Pulling from feisky/nginx
5e6ec7f28fb7: Pull complete
ab804f9bbcbe: Pull complete
052b395f16bc: Pull complete
56e01c98ad9d: Pull complete
6f031ef539d7: Pull complete
Digest: sha256:6c99de8c27d7e1ba440246fec9c99f0832a5d56990c7b2c32fb64be0face020a
Status: Downloaded newer image for feisky/nginx:latency
13f6b1af1b812440c4932d468eb76b7fb7956736297e35634e943d54fd7a12ce
# docker ps
CONTAINER ID   IMAGE                  COMMAND                  CREATED          STATUS          PORTS     NAMES
13f6b1af1b81   feisky/nginx:latency   "nginx -g 'daemon of…"   27 seconds ago   Up 27 seconds             latency
047094f369a3   nginx                  "/docker-entrypoint.…"   2 minutes ago    Up 2 minutes              good

运行以下命令以验证两个容器都在为流量提供服务:

# 测试正常nginx# curl http://127.0.0.1
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...
</html>
# 测试延迟nginx
# curl http://127.0.0.1:8080
...
<p><em>Thank you for using nginx.</em></p>
</body>
</html>

host2 准备

        现在让我们用上面提到的hping3来测试它们的延迟,看看有什么区别。在host2中,执行以下命令分别测试案例机的8080端口和80端口的延迟:

80 端口:

# hping3 -c 3 -S -p 80 192.168.47.128
HPING 192.168.47.128 (eth0 192.168.47.128): S set, 40 headers + 0 data bytes
len=46 ip=192.168.47.128 ttl=64 DF id=0 sport=80 flags=SA seq=0 win=64240 rtt=3.9 ms
len=46 ip=192.168.47.128 ttl=64 DF id=0 sport=80 flags=SA seq=1 win=64240 rtt=3.3 ms
len=46 ip=192.168.47.128 ttl=64 DF id=0 sport=80 flags=SA seq=2 win=64240 rtt=3.3 ms

--- 192.168.47.128 hping statistic ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 3.3/3.5/3.9 ms

8080 端口:

# 测试8080端口延迟
# hping3 -c 3 -S -p 8080 192.168.47.128
HPING 192.168.47.128 (eth0 192.168.47.128): S set, 40 headers + 0 data bytes
len=46 ip=192.168.47.128 ttl=64 DF id=0 sport=8080 flags=SA seq=0 win=64240 rtt=4.3 ms
len=46 ip=192.168.47.128 ttl=64 DF id=0 sport=8080 flags=SA seq=1 win=64240 rtt=7.6 ms
len=46 ip=192.168.47.128 ttl=64 DF id=0 sport=8080 flags=SA seq=2 win=64240 rtt=11.1 ms

--- 192.168.47.128 hping statistic ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 4.3/7.7/11.1 ms

        从这个输出中您可以看到80端口平均为3.5毫秒,8080端口平均为7.7毫秒。两者差别不是很大,但这仅适用于单个请求。并发请求怎么办?让我们用wrk试一试。

80 端口:

# wrk --latency -c 100 -t 2 --timeout 2 http://192.168.47.128/
Running 10s test @ http://192.168.47.128/
  2 threads and 100 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency    10.11ms    4.04ms  85.90ms   76.53%
    Req/Sec     5.02k   533.59     6.99k    76.00%
  Latency Distribution
     50%    9.61ms
     75%   11.68ms
     90%   14.84ms
     99%   22.60ms
  99820 requests in 10.02s, 81.20MB read
Requests/sec:   9963.87
Transfer/sec:      8.11MB

8080 端口:

# wrk --latency -c 100 -t 2 --timeout 2 http://192.168.47.128:8080/
Running 10s test @ http://192.168.47.128:8080/
  2 threads and 100 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency    44.92ms    6.74ms  67.51ms   94.57%
    Req/Sec     1.11k   144.41     2.00k    87.50%
  Latency Distribution
     50%   44.49ms
     75%   47.73ms
     90%   48.74ms
     99%   54.38ms
  22163 requests in 10.05s, 17.99MB read
Requests/sec:   2206.31
Transfer/sec:      1.79MB

        从以上两个输出可以看出,Nginx(监听 80 端口)的平均延迟为10.11ms,而延迟的Nginx(监听 8080 端口)的平均延迟为44.92ms。从延迟分布上来看,Nginx 可以在14.84ms内完成 90% 的请求;对于延迟的Nginx,50%的请求已经达到44.49ms。发生了什么?我们来做一些分析。

分析:

1. 在host1中,让我们使用tcpdump捕获一些网络数据包:

# tcpdump -nn tcp port 8080 -w nginx.pcap
tcpdump: listening on ens33, link-type EN10MB (Ethernet), capture size 262144 bytes

2.在host2上,重新运行wrk命令

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

        当wrk命令完成后,再次切换回host1,并按Ctrl+C结束tcpdump命令。然后,用Wireshark打开。

        由于网络包的数量很多,我们可以先过滤一下。在上面过滤信息填入:tcp.stream eq 24,如图:

        从这里,您可以看到从三次握手开始,此 TCP 连接的每个请求和响应。这可能不够直观,可以继续点击菜单栏中的 统计(Statistics) -> 流量图(Flow Graph),选择 “限制显示过滤器(Limit to display filter)”,将流类型(Flow type)设置为 “TCP Flows”:

        请注意,此图的左侧是客户端,而右侧是Nginx服务器。从这个图中可以看出,前三次握手和第一次HTTP请求和响应都相当快,但是第二次HTTP请求就比较慢了,尤其是客户端收到服务器的第一个数据包后,该ACK响应在 40ms 后(图中标注的0.078950比原来0.036211多了0.04)才被发送。这是TCP延迟ACK的最小超时。这是TCP ACK的一种优化机制,即不是每次请求都发送一个ACK,而是等待一段时间(比如 40ms),看看有没有“搭车”的数据包。如果在此期间还有其他数据包需要发送,它们将与ACK一起被发送。当然,如果等不及其他数据包,超时后会单独发送ACK。

        由于客户端发生了40ms延迟,可以怀疑客户端开启了延迟确认机制(Delayed Acknowledgment Mechanism)。这里的客户端其实就是之前运行的wrk。

        根据TCP文档,只有在TCP套接字专门设置了TCP_QUICKACK时才会启用快速确认模式(Fast Acknowledgment Mode);否则,默认使用延迟确认机制:

TCP_QUICKACK (since Linux 2.4.4)
Enable  quickack modeifsetordisablequickack modeifcleared.  In quickack mode, acks are sent imme‐
diately, rather than delayedifneededinaccordance to normal TCP operation.  This flag is  not  perma‐
nent,  it only enables a switch to or from quickack mode.  Subsequent operation of the TCP protocol will
once again enter/leave quickack mode depending on internal  protocol  processing  and  factors  such  as
delayed ack timeouts occurring and data transfer.  This option should not be usedincode intended to be
portable.

让我们测试一下我们的质疑:

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

        可以看到wrk只设置了TCP_NODELAY选项,没有设置TCP_QUICKACK。可以看到为什么延迟Nginx响应会出现一个延迟。

        那为什么80的那个nginx没有出现呢?我们进入两个nginx,查看配置

80的nginx

# docker exec -it 58f015e037bf bash
# cd /etc/nginx# cat nginx.config
user  nginx;

worker_processes  auto;
error_log  /var/log/nginx/error.log notice;
pid        /var/run/nginx.pid;
events {
        worker_connections  1024;
}

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    keepalive_timeout  65;

    #gzip  on;

    include /etc/nginx/conf.d/*.conf;
}

8080的nginx

# docker exec -it 13f6b1af1b81 bash
# cd /etc/nginx
# cat nginx.config
user  nginx;

worker_processes  1;
error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;
events {
        worker_connections  1024;
}

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile        on;
    tcp_nopush     off;
    tcp_nodelay    off;

    keepalive_timeout  65;

    #gzip  on;

    include /etc/nginx/conf.d/*.conf;
}

        看到了吗?标红的就是他们的差别,正常官方nginx的是注释了tcp_nopush这个配置的,而他默认是on,而延迟nginx是特别标注了tcp_nopush off和tcp_nodelay off

        软件的一个配置问题就可以导致软件性能多大的差距啊。至于worker_processes auto和worker_processes 1由于是虚拟机都没有设置那么多线程,差距效果也不会很明显。

结论

网络延迟是核心网络性能指标。由于网络传输、网络报文处理等多种因素的影响,网络延迟是不可避免的。但过多的网络延迟会直接影响用户体验。配置好软件,有可能解决一部分网络延迟的问题哦。

检验工具

  • 使用hping3和wrk等工具确认单个请求和并发请求的网络延迟是否正常。

  • 使用traceroute,确认路由正确,并查看路由中每个网关跳跃点的延迟。

  • 使用tcpdump和Wireshark确认网络数据包是否正常收发。

  • 使用strace等观察应用程序对网络socket的调用是否正常。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值