不同的 ACK 行为(降低吞吐量?)

不同的 ACK 行为(降低吞吐量?)

我正在运行 netio (http://freshmeat.net/projects/netio/) 在一台机器 (opensolaris) 上并联系两台不同的 Linux 机器(均在 2.6.18-128.el5 上),机器 A 和机器 B。机器 A 使用 netio 的网络吞吐量为 10MB/秒,机器 B 使用 netio 的网络吞吐量为 100MB/秒。在 open solaris 上,我对连接进行了 dtrace,所有交互看起来都相同 - 接收和发送的窗口大小相同,ssthresh 相同,拥塞窗口大小相同,但慢速机器每 2 或 3 次接收发送一次 ACK,而快速机器每 12 次接收发送一次 ACK。所有三台机器都在同一台交换机上。以下是 Dtrace 输出:快速机器:

delta 发送 接收  
 (美国)字节字节swnd snd_ws rwnd rcv_ws cwnd ssthresh
  122 1448 \ 195200 7 131768 2 128872 1073725440
   37 1448 \ 195200 7 131768 2 128872 1073725440
   20 1448 \ 195200 7 131768 2 128872 1073725440
   18 1448 \ 195200 7 131768 2 128872 1073725440
   18 1448 \ 195200 7 131768 2 128872 1073725440
   18 1448 \ 195200 7 131768 2 128872 1073725440
   18 1448 \ 195200 7 131768 2 128872 1073725440
   19 1448 \ 195200 7 131768 2 128872 1073725440
   18 1448 \ 195200 7 131768 2 128872 1073725440
   18 1448 \ 195200 7 131768 2 128872 1073725440  
   57 1448 \ 195200 7 131768 2 128872 1073725440
  171 1448 \ 195200 7 131768 2 128872 1073725440    
   29 912 \ 195200 7 131768 2 128872 1073725440   
   30 / 0 195200 7 131768 2 128872 1073725440  

慢速机器:

delta 发送 接收  
 (美国)字节字节swnd snd_ws rwnd rcv_ws cwnd ssthresh
  161 / 0 195200 7 131768 2 127424 1073725440  
   52 1448 \ 195200 7 131768 2 128872 1073725440
   33 1448 \ 195200 7 131768 2 128872 1073725440   
   11 1448 \ 195200 7 131768 2 128872 1073725440   
  143 / 0 195200 7 131768 2 128872 1073725440   
   46 1448 \ 195200 7 131768 2 130320 1073725440   
   31 1448 \ 195200 7 131768 2 130320 1073725440   
   11 1448 \ 195200 7 131768 2 130320 1073725440   
  157 / 0 195200 7 131768 2 130320 1073725440  
   46 1448 \ 195200 7 131768 2 131768 1073725440
   18 1448 \ 195200 7 131768 2 131768 1073725440

Dtrace 代码

dtrace:130717 在 CPU 0 上丢失
/usr/sbin/dtrace -s
#pragma D option quiet
#pragma D 选项默认参数
内联 int TICKS=$1;
内联字符串ADDR=$$2;
dtrace:::BEGIN
{
       计时器 = (TICKS != NULL )?TICKS : 1;
       滴答=计时器;
       标题 = 10;
       标题=0;
       墙上时间=时间戳;
       printf("正在启动...\n");
}
tcp:::发送
/(args[2]->ip_daddr == ADDR || ADDR == NULL)/
{
    nfs[args[1]->cs_cid]=1; /*这是一个 NFS 线程 */
    delta=时间戳-墙上时间;
    墙上时间=时间戳;
    printf("%6d %8d \ %8s %8d %8d %8d %8d %8d %12d %12d %12d %8d %8d %d \n",
        增量/1000,
        参数[2]->ip_plength - 参数[4]->tcp_offset,
        “”
        参数[3]->tcps_swnd,
        参数[3]->tcps_snd_ws,
        参数[3]->tcps_rwnd,
        参数[3]->tcps_rcv_ws,
        参数[3]->tcps_cwnd,
        参数[3]->tcps_cwnd_ssthresh,
        参数[3]->tcps_sack_fack,
        参数[3]->tcps_sack_snxt,
        参数[3]->tcps_rto,
        参数[3]->tcps_mss,
        参数[3]->tcps_retransmit
      (英文):
    标志=0;
    标题 - ;
}
tcp:::接收
/(args[2]->ip_saddr == ADDR || ADDR == NULL)&& nfs[args[1]->cs_cid] /
{
      delta=时间戳-挂钟时间;
      墙上时间=时间戳;

      printf("%6d %8s / %8d %8d %8d %8d %8d %8d %12d %12d %12d %8d %8d %d \n",
        增量/1000,
        “”
        参数[2]->ip_plength - 参数[4]->tcp_offset,
        参数[3]->tcps_swnd,
        参数[3]->tcps_snd_ws,
        参数[3]->tcps_rwnd,
        参数[3]->tcps_rcv_ws,
        参数[3]->tcps_cwnd,
        参数[3]->tcps_cwnd_ssthresh,
        参数[3]->tcps_sack_fack,
        参数[3]->tcps_sack_snxt,
        参数[3]->tcps_rto,
        参数[3]->tcps_mss,
        参数[3]->tcps_retransmit
      (英文):
    标志=0;
    标题 - ;
}

后续添加了未确认字节数,结果发现慢速代码确实会耗尽其未确认字节数,直到达到拥塞窗口,而快速机器永远不会达到其拥塞窗口。以下是慢速机器在其未确认字节达到拥塞窗口时的输出:

unack unack delta bytes 字节数 发送 接收 cong ssthresh
字节 字节 我们 发送 接收 窗口 窗口 窗口
已发送 已收到
139760 0 31 1448 \ 195200 131768 144800 1073725440
139760 0 33 1448 \ 195200 131768 144800 1073725440
144104 0 29 1448 \ 195200 131768 146248 1073725440
145552 0 31 / 0 195200 131768 144800 1073725440
145552 0 41 1448 \ 195200 131768 147696 1073725440
147000 0 30 / 0 195200 131768 144800 1073725440
147000 0 22 1448 \ 195200 131768 76744 72400
147000 0 28 / 0 195200 131768 76744 72400
147000 0 18 1448 \ 195200 131768 76744 72400
147000 0 26 / 0 195200 131768 76744 72400
147000 0 17 1448 \ 195200 131768 76744 72400
147000 0 27 / 0 195200 131768 76744 72400
147000 0 18 1448 \ 195200 131768 76744 72400
147000 0 56 / 0 195200 131768 76744 72400
147000 0 22 1448 \ 195200 131768 76744 72400

dtrace 代码:

/usr/sbin/dtrace -s
#pragma D option quiet
#pragma D 选项默认参数
内联 int TICKS=$1;
内联字符串ADDR=$$2;
tcp:::发送,tcp:::接收
/(args[2]->ip_daddr == ADDR || ADDR == NULL)/
{
    nfs[args[1]->cs_cid]=1; /*这是一个 NFS 线程 */
    delta=时间戳-墙上时间;
    墙上时间=时间戳;
    printf("%6d %6d %6d %8d \ %8s %8d %8d %8d %8d %8d %12d %12d %12d %8d %8d %d \n",
        参数[3]->tcps_snxt - 参数[3]->tcps_suna
        参数[3]->tcps_rnxt - 参数[3]->tcps_rack,
        增量/1000,
        参数[2]->ip_plength - 参数[4]->tcp_offset,
        “”
        参数[3]->tcps_swnd,
        参数[3]->tcps_snd_ws,
        参数[3]->tcps_rwnd,
        参数[3]->tcps_rcv_ws,
        参数[3]->tcps_cwnd,
        参数[3]->tcps_cwnd_ssthresh,
        参数[3]->tcps_sack_fack,
        参数[3]->tcps_sack_snxt,
        参数[3]->tcps_rto,
        参数[3]->tcps_mss,
        参数[3]->tcps_retransmit
      (英文):
}
tcp:::接收
/(args[2]->ip_saddr == ADDR || ADDR == NULL)&& nfs[args[1]->cs_cid] /
{
      delta=时间戳-挂钟时间;
      墙上时间=时间戳;
      printf("%6d %6d %6d %8s / %-8d %8d %8d %8d %8d %8d %12d %12d %12d %8d %8d %d \n",
        参数[3]->tcps_snxt - 参数[3]->tcps_suna
        参数[3]->tcps_rnxt - 参数[3]->tcps_rack,
        增量/1000,
        “”
        参数[2]->ip_plength - 参数[4]->tcp_offset,
        参数[3]->tcps_swnd,
        参数[3]->tcps_snd_ws,
        参数[3]->tcps_rwnd,
        参数[3]->tcps_rcv_ws,
        参数[3]->tcps_cwnd,
        参数[3]->tcps_cwnd_ssthresh,
        参数[3]->tcps_sack_fack,
        参数[3]->tcps_sack_snxt,
        参数[3]->tcps_rto,
        参数[3]->tcps_mss,
        参数[3]->tcps_retransmit
      (英文):
}

现在仍然是一个问题,为什么一台机器落后而另一台没有落后......

答案1

我以前也见过类似的行为。我发现有两个原因:

  • TCP/IP 流控制协商不良
  • 糟糕的司机

在您的情况下,TCP/IP 流控制问题不太可能发生,因为两台机器都运行相同的内核(如果不同,则设备内核模块除外),因此运行相同的 TCP/IP 代码。

不过,是司机。

不久前,我有一台 Windows 2003 服务器,它无法向某些服务器传输超过 6-10MB/s 的数据,而且由于这是一台备份到磁盘的服务器,这简直是不可接受的。查看了一些数据包捕获后,它们看起来非常像您看到的内容。修复方法是将接收服务器(Server 2003 备份服务器)上的网络驱动程序(碰巧是 Broadcom)更新为较新的版本。完成此操作后,我获得了 60-80MB/s 的数据。

由于这是 Linux,你可能会遇到大段卸载某种问题。这在一定程度上依赖于 NIC 硬件本身处理大段的分割。如果由于某种原因(固件不好?)无法正常工作,则会导致这些奇怪的延迟。这是基于每个驱动程序或接口配置的。ethtool -K可以通过设备进行配置。

相关内容