10 Network

10 Network随着系统变得更加分布式,特别是在云计算环境中,网络在性能中扮演着更重要的角色。除了改善网络延迟和吞吐量外,另一个常见任务是消除由丢包引起的延迟异常。 网络分析涵盖硬件和软件。硬件

有两个因素共同阻碍了巨型帧的采用:老化的网络硬件和错误配置的防火墙。不支持巨型帧的旧硬件可以使用IP 协议对数据包进行分段,或者通过通知发送方减小数据包大小来响应ICMP“无法分段”错误。这就是配置错误的防火墙发挥作用的地方。基于ICMP 的攻击(包括“死亡之ping”)过去曾发生过,一些防火墙管理员的应对措施是阻止所有ICMP。这可以防止有用的“不可分段”消息传递给发送方,并在数据包大小超过1,500 时默默地丢弃网络数据包。

为了避免此问题,许多系统坚持使用MTU 默认值1,500。 TCP 卸载和大段卸载等网络接口卡功能可提高1,500 MTU 帧的性能。这些功能将更大的缓冲区发送到网卡,并使用专门的优化硬件将缓冲区分割成更小的帧。这在一定程度上缩小了1,500 MTU 和9,000 MTU 网络之间的性能差距。

10.3.5 延迟

延迟是网络性能的关键指标,可以通过多种方式进行测量,包括名称解析延迟、ping 延迟、连接延迟、首字节延迟、往返时间和连接生命周期。这些是客户端连接到服务器时进行的测量。

名称解析延迟

建立与远程主机的连接时,主机名通常会解析为IP 地址,例如通过DNS 解析。所花费的时间可以作为名称解析延迟单独测量。在最坏的情况下,这种延迟可能会导致名称解析超时,可能需要数十秒。

在某些情况下,应用程序的运行可能不需要名称解析,可以禁用名称解析以避免这种延迟。

Ping 延迟

这是ICMP 回显请求和回显应答之间的时间,由ping(1) 命令测量。该时间是主机之间网络延迟(包括中间跃点)的度量,并以数据包往返所需的时间来衡量。它被广泛使用,因为它简单、易于执行并且通常容易获得。许多操作系统默认响应ping。

表10.1 显示了ping 延迟示例。为了更好地说明所涉及的数量级,缩放列显示了基于1 秒假设本地主机ping 延迟的比较。

在接收端,ICMP 回显请求通常在中断上下文中处理并立即返回,从而为内核代码执行增加最少的额外时间。在发送端,时间戳是从用户模式测量的,因此它们可能包括一些额外的时间来考虑内核上下文切换和内核代码传递时间。

连接延迟

连接延迟是指在传输数据之前建立网络连接所需的时间。对于TCP 连接延迟,这是TCP 握手时间。从客户端测量的发送SYN 和接收相应SYN-ACK 之间的时间。连接延迟通常称为连接建立延迟,以将其与连接生存期明确区分开来。

连接延迟与ping 延迟类似,但会调用更多内核代码来建立连接,并包括重新传输丢失数据包的时间。特别是,如果服务器的等待队列已满,TCP SYN 数据包会被服务器丢弃,导致客户端重新发送SYN。这种情况发生在TCP 握手期间,因此连接延迟(包括重传延迟)会增加一秒多。

连接延迟之后是第一个字节延迟。

第一个字节延迟

第一个字节的延迟,也称为第一个字节的时间(TTFB),是建立连接和接收第一个字节之间的时间。这包括远程主机接受连接的时间、调度服务线程的时间以及线程运行和发送第一个字节的时间。

Ping 和连接延迟测量网络产生的延迟,而第一个字节延迟包括目标服务器的思考时间。这可以包括服务器过载时处理请求所需的时间(例如TCP 等待队列)或调度服务器所需的时间(CPU 执行队列延迟)。

往返时间

往返时间表示网络数据包在端点之间传输所需的时间。

连接寿命

连接生存期是建立网络连接和关闭网络连接之间的时间。一些协议使用保活策略来延长连接持续时间,以便现有连接可以用于将来的操作,避免连接建立的开销和延迟。

10.3.6 缓冲

通过在发送方和接收方都使用缓冲区,尽管可能会出现不同的网络延迟,但可以保持快速的网络吞吐量。较大的缓冲区可以通过在阻塞和等待确认之前继续发送数据来减少往返时间增加的影响。

TCP 使用缓冲区和滑动发送窗口来提高吞吐量。网络套接字也有缓冲区,应用程序也可以在发送数据之前使用缓冲区来聚合数据。

缓冲也可以由外部网络组件(例如交换机和路由器)执行,以增加其自身的吞吐量。不幸的是,在这些组件中使用大缓冲区可能会导致称为缓冲区膨胀的问题,即数据包长时间排队。这可以避免主机上的TCP 拥塞并限制性能。 Linux 3.x 内核添加了解决此问题的功能(字节队列限制、CoDel 队列管理[Nichols 12]、TCP 小队列等),并且有多个网站正在讨论此问题[1]。

根据称为端到端调整[Saltzer 84] 的原则,缓冲(或大规模缓冲)功能可能最好由端点(主机)而不是中间网络节点提供。

10.3.7 连接积压

另一种类型的缓冲用于初始连接请求。 TCP 实现了一个后备队列,SYN 请求在被用户空间进程接受之前在内核中排队。如果TCP 连接请求过多,进程无法及时接受,则备份队列将达到其限制,SYN 数据包将被客户端丢弃并稍后重新发送。重新传输这些数据包可能会导致客户端连接延迟。测量后端队列丢弃是测量网络连接饱和度的一种方法。

10.3.8 接口协商

网络接口可以在不同模式下运行并与其他端点自动协商。示例包括:

带宽:示例:10、100、1,000、10,000 Mbits/s

双工:半双工或全双工

这些示例来自基于十进制的以太网,该以太网倾向于使用舍入来限制带宽。其他物理层协议(例如SONET)具有不同的可用带宽集。

网络接口通常用最高带宽和协议来描述,例如1 Gbit/s 以太网(1 GbE)。但是,如有必要,接口可以自动协商到较低的速度。如果另一个端点无法以更快的速度运行,或者连接介质无法适应物理问题(例如布线不良),则可能会发生这种情况。

全双工模式允许使用单独的发送和接收路径在两个方向上同时进行传输,并且可以在全带宽下运行。半双工模式一次只允许在一个方向上进行传输。

10.3.9 用法

网络接口利用率可以通过当前吞吐量除以最大带宽来计算。计算这个指标并不像看起来那么容易,因为自动协商会改变带宽和双工模式。

– 快速恢复:通过重置连接并在检测到重复的ACK 后执行慢启动来恢复TCP 性能。在某些情况下,这些功能是通过添加到协议标头的扩展TCP 选项来实现的。

TCP 性能中的重要主题包括三向握手、重复ACK 检测、拥塞控制算法、Nagle 算法、延迟确认、SACK 和FACK。

3次握手

主机之间使用三向握手来建立连接。一台主机被动侦听连接,另一台主机主动发起连接。为清楚起见,术语:被动和主动来自[RFC 793],但根据套接字API,通常分别称为侦听和连接。在客户端/服务器模型中,服务器执行监听,客户端执行连接。三向握手如图10.6所示。

指示客户端连接延迟并在发送最后一个ACK 时完成。然后就可以开始数据传输了。该图显示了握手的最佳情况延迟。如果超时并重新传输,数据包可能会被丢弃并增加延迟。

重复ACK检测

重复ACK检测由快速重传和快速恢复算法使用。这是在发送端完成的,工作原理如下:

1. 发送方发送序列号为10的数据包。

2. 接收方用序列号11 的ACK 进行响应。

3. 发送方发送11、12、13。

4. 数据包11 被丢弃。

5. 接收方仍然期望11,因此它发送11 的ACK 并响应12 和13。

6. 发送方收到重复的11 个ACK。

重复ACK 检测也用于TCP Reno 和Tahoe 拥塞避免算法。

拥塞控制:里诺和太浩湖

这些拥塞控制算法首先在4.3BSD 中实现。

– Reno:三次重复ACK触发:一半拥塞窗口、一半慢启动阈值、快速重传、快速恢复。

– Tahoe:ACK 重复3 次触发:快速重传、慢启动阈值减半、拥塞窗口设置为最大分段大小(MSS)、慢启动条件。

某些操作系统(例如Linux 和Oracle Solaris 11)允许您选择算法作为系统调整的一部分。为TCP 开发的新算法包括Vegas、New Reno 和Hybla。

内格尔

该算法[RFC 896] 通过延迟小数据包的传输以允许更多数据到达并合并来减少网络上小数据包的数量。仅当管道中有数据并且数据包已经延迟时,数据包才会延迟。系统可以提供可调参数来禁用Nagle。如果Nagle 的行为与延迟ACK 冲突,您可能需要禁用Nagle。

延迟确认

该算法[RFC 1122] 将确认ACK 的发送延迟最多500 毫秒,以允许组合多个ACK。它还可以与其他TCP 控制消息结合使用,以减少网络上的数据包数量。

吮吸和他妈的

TCP 选择性确认(SACK) 算法允许接收方通知发送方它已收到非连续的数据块。如果没有此功能,数据包丢失最终将导致整个传输窗口重新传输,从而保留顺序确认方案。这会对TCP 性能产生负面影响,并且在大多数支持SACK 的现代操作系统中都可以避免。

SACK 已通过前向确认(FACK) 进行了扩展,Linux 默认支持FACK。 FACK 跟踪额外的状态并更好地平衡网络中未完成的数据量,从而提高整体性能[数学家96]。

UDP协议

用户数据报协议(UDP) 是一种常用的Internet 标准,用于通过网络发送称为数据报的消息[RFC 768]。在性能方面,UDP具有以下特点:

简单性:简单且小的协议标头减少了计算和大小开销。

无状态:连接和传输开销低。

无重传:这会显着增加TCP 连接的延迟。

尽管简单且通常性能良好,但UDP 并不旨在提供可靠性,并且数据可能会丢失或乱序。这使得它不适合许多类型的连接。另外,UDP没有拥塞避免机制,会导致网络拥塞。

某些服务(包括某些版本的NFS)可以配置为通过TCP 或UDP 运行,具体取决于您的需要。执行广播或多播数据的其他服务可能仅使用UDP。

10.4.2 硬件

网络硬件包括接口、控制器、交换机和路由器。即使这些组件中的任何一个由其他人(网络管理员)管理,了解它们的工作原理也是有用的。

界面

物理网络接口通过连接的网络发送和接收称为帧的消息。它们管理相关的电、光或无线电信号传输,包括传输错误的处理。

接口类型基于第2 层标准,每种类型都提供最大带宽。一般来说,带宽越高的接口延迟越低,但成本也越高。在设计新服务器时,这通常是一个重要的选择,以平衡服务器的价格与所需的网络性能。

对于以太网,您可以选择有线或光纤,速度高达1 Gbit/s (1 GbE)、10 GbE、40 GbE 或100 GbE。许多供应商制造以太网接口控制器,但操作系统可能不支持某些控制器的驱动程序。

您可以通过当前协商带宽除以当前吞吐量来查看接口的利用率。大多数接口都有独立的发送和接收通道,在全双工模式下运行时必须单独考虑每个通道的利用率。

控制器

通过控制器向系统提供物理网络接口。控制器可以集成到系统主板中或通过扩展卡提供。

该控制器由微处理器供电,并通过PCI 等I/O 传输连接到系统。所有这些都可能成为网络吞吐量或IOPS 的限制因素。

例如,双端口10 GbE 网络接口卡连接到四通道PCI Express (PCIe) Gen 2 插槽。该卡的最大带宽为2 x 10 GbE=20 Gbit/s。每个槽位的最大带宽为4 x 4 Gbits/s=16 Gbits/s。因此,两个端口的网络吞吐量都受到PCIe Gen 2 带宽的限制,并且您无法同时以线速驱动这两个端口(我从实践中知道这一点!)。

交换机和路由器

交换机在两个连接的主机之间提供专用通信路径,从而允许多个主机对之间的无干扰传输。该技术取代了与所有主机共享所有数据包的集线器(以及以前共享的物理总线,例如粗以太网同轴电缆)。当主机同时传输时,这种共享会导致冲突。接口使用“带冲突检测的载波侦听多路访问”(CSMA/CD) 算法将其识别为冲突,并且可能会按指数方式回退并重新传输,直到成功为止。此行为可能会导致负载下的性能问题。通过使用交换机,这些问题已成为过去,但监视工具仍然具有争用计数器,即使它们通常仅由错误(协商或错误接线)引起。

路由器在网络之间传递数据包,并使用网络协议和路由表来确定有效的传送路径。在两个城市之间传递数据包可能需要十多个路由器和其他网络硬件。路由器和路由通常配置为动态更新,允许网络自动响应网络和路由器故障并平衡负载。这意味着没有人可以确定数据包在任何给定时间实际采取的路径。由于存在多个可能的路径,数据包也可能会无序传送,这可能会导致TCP 性能问题。网络上的这种神秘元素通常是导致性能不佳的原因。也许来自其他不相关主机的大量网络流量使源和目标之间的路由器饱和?因此,网络管理团队通常不需要承担基础设施的责任。使用先进的实时监控工具调查所有相关路由器和其他网络组件。

路由器和交换机都具有微处理器,其本身可能成为负载下的性能瓶颈。作为一个极端的例子,由于CPU 容量有限,早期的10 GbE 交换机被发现无法超过11 Gbit/s 的聚合带宽。

其他设备

您的环境可能包括其他物理网络设备,例如集线器、网桥、中继器和调制解调器。这两者都可能成为性能瓶颈并导致数据包丢失。

10.4.3 软件

网络软件包括网络堆栈、TCP 和设备驱动程序。本节涵盖与性能相关的主题。

网络堆栈

所涉及的组件和层根据所使用的操作系统类型、版本、协议和接口的不同而有所不同。图10.7 显示了软件组件的通用模型。

在现代内核中,堆栈是多线程的,传入的数据包可以由多个CPU 处理。有多种方法可以将传入数据包映射到CPU。它可能基于源IP 地址的哈希来均匀分配负载,也可能基于最后处理该套接字的CPU 进行缓存。热量可从CPU 和内存局部性中受益。基于Linux 和基于Solaris 的系统都有不同的框架来支持此行为。

Linux

在Linux系统上,TCP、IP和通用网络驱动软件是核心内核组件,设备驱动程序是附加模块。数据包使用struct sk_buff 数据类型通过这些内核组件。

图10.8 更详细地显示了通用网络驱动程序,包括New API (NAPI) 接口,它通过组合中断来提高性能。

高数据包速率是通过利用多个CPU 处理数据包和TCP/IP 堆栈来实现的。此内容记录在Linux 3.7 内核文档(Documentation/networking/scaling.txt) 中,包括:

– RSS:接收端扩展:有利于支持多个队列并允许数据包散列到不同的队列。

的现代网卡,这些数据包通过直接中断不同的CPU来处理。此哈希可能基于IP地址和TCP端口号,以便来自同一连接的数据包最终由同一CPU处理。
– RPS:接收数据包定向(Receive Packet Steering):RSS的软件实现,适用于不支持多队列的网卡。这涉及一个简短的中断服务例程,将入站数据包映射到CPU进行处理。可以使用类似的哈希将数据包映射到CPU,基于数据包头字段。
– RFS:接收流定向(Receive Flow Steering):类似于RPS,但具有对套接字上一次在CPU上处理的亲和性,以提高CPU缓存命中率和内存局部性。
– 加速接收流定向(Accelerated Receive Flow Steering):这在支持此功能的网卡上通过硬件实现RFS。它涉及向网卡更新流信息,以便确定要中断哪个CPU。
– XPS:传输数据包定向(Transmit Packet Steering):对于具有多个传输队列的网卡,此功能支持由多个CPU向队列传输。
如果没有针对网络数据包的CPU负载平衡策略,网卡可能只会中断一个CPU,这可能会达到100%的利用率并成为瓶颈。根据诸如缓存一致性之类的因素将中断映射到CPU,就像RFS所做的那样,可以显着提高网络性能。irqbalancer进程也可以完成这个任务,它将中断请求(IRQ)线路分配给CPU。
Solaris
在基于Solaris的系统中,套接字层是 sockfs 内核模块,而 TCP、UDP 和 IP 协议被合并到 ip 模块中。数据包通过内核作为消息块 mblk_t 传递。更详细地展示了底层堆栈,如图10.9所示 [McDougall 06a]。

GLDv3 软件还通过垂直边界(vertical perimeters)提高了性能:这是与连接相关的每个 CPU 同步机制,避免了网络堆栈中每个数据结构的锁需求。这使用了一个称为序列化队列(squeue)的抽象,它处理每个连接。
通过启用 IP fanout 可以实现高数据包速率,该功能可以在多个 CPU 之间负载均衡入站数据包。
最近,Erik Nordmark 在 Solaris IP 数据通路重构项目中简化了网络堆栈的内部。关于之前堆栈状态的描述,包括性能[2],来自该项目:
IP 数据通路非常难以理解……这使得甚至修复代码中的错误都变得困难,更不用说使其性能达到最佳状态了。通过创建许多快速路径,即完整数据通路的子集,来改善性能。这进一步使代码维护成为一项危险的活动。
该项目已经集成到 snv_122,并将 IP 代码从 140,000 行减少了 34,000 行。
TCP
TCP 协议之前已经描述过。本节描述了内核 TCP 实现的性能特性:积压队列和缓冲区。
通过使用积压队列来处理连接的突发性增加。有两个这样的队列,一个用于在 TCP 握手完成之前的未完成连接(也称为 SYN 积压),另一个用于已建立会话等待被应用程序接受的已建立会话(也称为监听积压)。这些在图10.10中显示。

在早期的内核中只使用了一个队列,并且容易受到 SYN 洪水攻击的影响。SYN 洪水是一种 DoS 攻击类型,涉及向虚假 IP 地址发送大量的 SYN,以连接到监听 TCP 端口。这会在 TCP 等待完成握手时填满积压队列,阻止真实客户端连接。
有了两个队列,第一个可以作为潜在虚假连接的暂存区,只有在连接建立后才将其提升到第二个队列。第一个队列可以被设置得很长,以吸收 SYN 洪水,并优化为仅存储必要的最小元数据。
这些队列的长度可以独立调整(参见第10.8节,调整)。第二个队列也可以由应用程序设置为监听(listen())的积压参数。
通过使用与套接字关联的发送和接收缓冲区来提高数据吞吐量。这些在图10.11中显示。

对于写路径,数据被缓存在TCP发送缓冲区中,然后发送到IP进行传送。虽然IP协议具有分片数据包的能力,但TCP通过将数据作为MSS大小的段发送到IP来尽量避免这种情况。这意味着(重新)传输的单位与分片的单位相匹配;否则,丢失的分片将需要重新传输整个预分片的数据包。这种方法还可以提高TCP/IP堆栈的效率,因为它避免了常规数据包的分片和重组。
发送和接收缓冲区的大小都是可调整的。较大的大小会提高吞吐量性能,但每个连接消耗的主存更多。如果预计服务器需要更多发送或接收,则可以将其中一个缓冲区设置为较大。Linux内核还会根据连接活动动态增加这些缓冲区的大小。
网络设备驱动
网络设备驱动程序通常具有额外的缓冲区——环形缓冲区——用于在内核内存和网卡之间发送和接收数据包。
随着10GbE网络的引入,一个越来越常见的性能特性是使用中断合并模式。不是为每个到达的数据包中断内核,而是仅在定时器(轮询)到达或达到一定数量的数据包时才发送中断。这降低了内核与网卡通信的速率,允许更大的传输被缓冲,从而提高了吞吐量,尽管在延迟方面会付出一些代价。在基于Solaris的内核中,这被称为动态轮询。
10.5 Methodology
本节介绍了网络分析和调优的各种方法和练习。表10.2总结了这些主题。

有关更多策略和其中许多内容的介绍,请参阅第2章“方法论”。
这些方法可以单独跟随,也可以组合使用。我建议首先按照以下顺序使用以下策略:性能监控,USE方法,静态性能调优和工作负载特征化。
第10.6节“分析”展示了应用这些方法的操作系统工具。
10.5.1 Tools Method
工具方法是一个迭代使用可用工具、检查它们提供的关键指标的过程。这种方法可能会忽视工具未能提供良好或没有可见性的问题,并且可能需要花费大量时间来执行。
对于网络,工具方法可能涉及以下检查:
netstat -s:查看重传率和乱序数据包的高速率。对于何为“高”重传率取决于客户端:面向互联网的系统与不可靠的远程客户端应该具有比同一数据中心内的内部系统更高的重传率。
netstat -i:检查接口错误计数器(特定计数器取决于操作系统版本)。
ifconfig(仅适用于Linux版本):检查“errors”,“dropped”,“overruns”。
吞吐量:检查字节传输和接收的速率——在Linux上,通过ip(8);在Solaris上,通过nicstat(1)或dladm(1M)。高吞吐量可能会达到协商速度的线速并受到限制。它还可能在系统上的网络用户之间造成争用和延迟。
tcpdump/snoop:虽然在CPU成本方面可能昂贵,但在短时间内使用它们可能足以查看谁在使用网络,并识别可以消除的不必要工作。
dtrace/stap/perf:用于在应用程序和线路之间选择性地检查数据包,包括检查内核状态。
如果发现问题,请从可用工具的所有字段中检查以了解更多上下文。有关每个工具的更多信息,请参见第10.6节“分析”。还可以使用其他方法,这些方法可以识别更多类型的问题。
10.5.2 USE Method
USE方法用于快速识别所有组件中的瓶颈和错误。对于每个网络接口,在每个方向(发送(TX)和接收(RX))检查以下内容:
利用率(Utilization):接口忙于发送或接收帧的时间。
饱和度(Saturation):由于完全利用的接口而产生的额外排队、缓冲或阻塞程度。
错误(Errors):对于接收端:坏校验和、帧太短(小于数据链路头)或太长、碰撞(在交换网络中不太可能);对于发送端:迟到的碰撞(坏的布线)。
错误可能首先进行检查,因为它们通常很快检查完毕并且易于解释。
利用率通常不会直接由操作系统或监控工具提供。可以将其计算为每个方向(RX、TX)的当前吞吐量除以当前协商速度。当前吞吐量应以字节每秒的形式在网络上传输,包括所有协议头。
对于实施网络带宽限制(资源控制)的环境,如某些云计算环境中所发生的,网络利用率可能需要根据所施加的限制来衡量,而不仅仅是物理限制。
网络接口的饱和度很难测量。一些网络缓冲是正常的,因为应用程序可以比接口更快地发送数据。可能可以将其测量为应用程序线程在网络发送上被阻塞的时间,随着饱和度增加,这个时间可能会增加。此外,还要检查是否有与接口饱和度更密切相关的其他内核统计信息,例如,Linux的“overruns”或Solaris的“nocanputs”。
在TCP级别上的重传通常作为统计数据很容易获得,并且可以作为网络饱和度的指标。但是,它们是在服务器与其客户端之间的网络上测量的,可能发生在任何跳跃点。
USE方法也可以应用于网络控制器及其与处理器之间的传输。由于这些组件的可观察性工具稀少,根据网络接口统计信息和拓扑推断指标可能更容易。例如,如果网络控制器A包含端口A0和A1,则可以将网络控制器吞吐量计算为接口吞吐量A0 + A1的总和。有了已知的最大吞吐量,然后可以计算网络控制器的利用率。
10.5.3 Workload Characterization
对应用的负载进行表征是容量规划、基准测试和模拟工作负载的重要练习。通过识别可以消除的不必要工作,它还可以带来一些最大的性能增益。以下是用于表征网络工作负载的基本属性,它们可以共同提供对网络被要求执行的近似描述:
– 网络接口吞吐量:接收(RX)和发送(TX),每秒字节数
– 网络接口IOPS(每秒输入/输出操作数):接收(RX)和发送(TX),每秒帧数
– TCP连接速率:活动和被动连接,每秒连接数
\”活动\”和\”被动\”这两个术语在“三次握手”章节中有描述。
这些特性随着时间的推移可能会发生变化,因为一天中的使用模式会发生变化。关于随时间的监控在\”性能监控\”章节中有描述。以下是一个示例工作负载描述,展示了如何将这些属性结合在一起表达:
网络吞吐量基于用户的不同而变化,执行更多的写入(TX)比读取(RX)。峰值写入速率为每秒200兆字节和每秒210,000个数据包,峰值读取速率为每秒10兆字节和每秒70,000个数据包。入站(被动)TCP连接速率达到每秒3,000个连接。
除了对系统范围内描述这些特性外,它们也可以针对每个接口进行表达。如果观察到吞吐量已达到线路速率,这允许确定接口瓶颈。如果存在网络带宽限制(资源控制),则可能在达到线路速率之前会限制网络吞吐量。
高级工作负载表征/检查清单还可以包含其他细节以表征工作负载。这些被列为考虑的问题,也可以在彻底研究CPU问题时作为检查清单:
– 平均数据包大小是多少?RX、TX?
– 协议分布是什么?TCP与UDP?
– 活动的TCP/UDP端口是哪些?每秒字节数、每秒连接数?
– 哪些进程正在积极使用网络?
随后的章节回答了其中一些问题。有关此方法论和要测量的特性的更高级别摘要,请参阅第2章“方法论”。
10.5.4 Latency Analysis
有各种不同的时间(延迟)可以研究,以帮助理解和表达网络性能。它们包括网络延迟——一个稍微模糊的术语,通常用来指代连接初始化时间。各种网络延迟总结如表10.3所示。

其中一些延迟在第10.3节“概念”中有详细描述。延迟可以呈现为:
– 每个时间间隔的平均值:最好针对每个客户端/服务器对执行,以隔离中间网络中的差异
– 完整分布:作为直方图或热力图
– 每个操作的延迟:列出每个事件的详细信息,包括源和目标IP地址
问题的常见来源是由TCP重传引起的延迟异常值。这些可以使用完整分布或每个操作的延迟跟踪来识别,包括通过过滤最小延迟阈值来进行。
10.5.5 Performance Monitoring
性能监控可以识别出时间上的活动问题和行为模式。它将捕获活跃终端用户数量的变化,计时活动,包括分布式系统监控,以及通过网络进行的应用活动,包括备份。
网络监控的关键指标包括:
– 吞吐量:每秒接收和发送的网络接口字节数,最好是针对每个接口
– 连接:每秒TCP连接数,作为网络负载的另一个指标
– 错误:包括丢包计数器
– TCP重传:也有助于记录以与网络问题进行相关性分析
– TCP乱序数据包:也可能导致性能问题
对于实施网络带宽限制(资源控制)的环境,例如某些云计算环境,还可以收集与施加的限制相关的统计信息。
10.5.6 Packet Sniffing
数据包嗅探(又称数据包捕获)涉及从网络中捕获数据包,以便可以逐个数据包地检查其协议头和数据。对于观察性分析而言,这可能是最后的手段,因为从CPU和存储开销方面来看,执行起来可能会很昂贵。网络内核代码路径通常经过循环优化,因为它们需要处理每秒高达数百万个数据包,并且对任何额外的开销都很敏感。为了尝试减少这种开销,内核可能会使用环形缓冲区通过共享内存映射将数据包数据传递给用户级别的跟踪工具,例如,Linux的PF_RING选项而不是每个数据包的PF_PACKET [Deri 04]。
可以在服务器上创建一个数据包捕获日志,然后使用其他工具进行分析。有些工具仅打印内容;其他工具对数据包数据执行更高级别的分析。虽然阅读数据包捕获日志可能会耗费时间,但也可能会非常有启发性——准确显示了网络上正在发生的事情,以及数据包之间的延迟。这使得可以应用工作负载特征化和延迟分析方法。
数据包捕获日志可以包含以下内容:
– 时间戳
– 整个数据包,包括
  – 所有协议头(例如以太网、IP、TCP)
  – 部分或完整的有效载荷数据
– 元数据:数据包数量、丢包数量
作为数据包捕获的示例,以下显示了tcpdump工具的默认输出。

该输出包含一行总结每个数据包,包括IP地址、TCP端口和其他TCP头部详情的细节。
由于数据包捕获可能是一项消耗CPU资源的活动,大多数实现都包括在负载过重时放弃捕获事件而不是捕获它们的能力。丢弃数据包的计数可能会包含在日志中。
除了使用环形缓冲区外,数据包捕获实现通常允许用户提供过滤表达式,并在内核中执行此过滤。这通过不将不需要的数据包传输到用户级别来减少开销。
10.5.7 TCP Analysis
除了在第10.5.4节“延迟分析”中讨论的内容外,还可以调查其他特定的TCP行为,包括:
– TCP发送/接收缓冲区的使用情况
– TCP后台队列(backlog queues)的使用情况
– 由于后台队列已满而导致内核丢弃的数据包
– 拥塞窗口大小,包括零大小的广告(zero-size advertisements)
– 在TCP TIME-WAIT1间隔期间收到的SYN包
在同一目标端口上频繁连接到另一个服务器时,这些行为可能会成为可探究的问题,使用相同的源和目标IP地址。每个连接的唯一区分因素是客户端源端口(临时端口),对于TCP来说,这是一个16位值,并且可能会受到操作系统参数(最小和最大值)的进一步限制。
结合TCP TIME-WAIT间隔(可能为60秒),在60秒内高速连接的数量(超过65,536个)可能会遇到新连接的冲突。在这种情况下,当临时端口仍然与处于TIME-WAIT状态的先前TCP会话关联时发送了SYN包,如果新的SYN包被误识别为旧连接的一部分(发生冲突),则可能会被拒绝。为了避免这个问题,Linux内核会尝试快速重用或回收连接(通常效果良好)。
10.5.8 Drill-Down Analysis
可以根据需要调查内核网络堆栈的内部情况,通过逐层深入到处理数据包的网络接口驱动程序。内部结构复杂,这是一项耗时的活动。进行此活动的原因包括:
– 检查是否需要调整网络可调参数(而不是进行实验性修改)
– 确认内核网络性能特性是否生效,包括例如CPU分流和中断合并
– 解释内核丢弃的数据包
通常涉及使用动态跟踪来检查内核网络堆栈函数的执行情况。
10.5.9 Static Performance Tuning
静态性能调优侧重于已配置环境的问题。对于网络性能,要检查静态配置的以下方面:
– 可供使用的网络接口数量是多少?当前有多少在使用中?
– 网络接口的最大速度是多少?
– 网络接口的当前协商速度是多少?
– 网络接口是半双工还是全双工协商的?
– 网络接口配置了什么MTU?
– 网络接口是否进行了干线聚合(trunked)?
– 设备驱动程序、IP层和TCP层存在哪些可调参数?
– 是否有任何可调参数已从默认值更改?
– 路由是如何配置的?默认网关是什么?
– 数据路径中网络组件的最大吞吐量是多少(所有组件,包括交换机和路由器背板)?
– 转发是否已启用?系统是否作为路由器运行?
– DNS配置是如何的?服务器距离有多远?
– 网络接口固件的版本是否已知存在性能问题(bug)?
– 网络设备驱动程序或内核TCP/IP堆栈的版本是否已知存在性能问题(bug)?
– 是否存在软件实施的网络吞吐量限制(资源控制)?它们是什么?
这些问题的答案可能会揭示被忽视的配置选择。最后一个问题尤其适用于云计算环境,因为网络吞吐量可能受到限制。
10.5.10 Resource Controls
操作系统可能提供控制,以限制连接类型、进程或进程组的网络资源。这些控制可能包括以下类型:
– 网络带宽限制:由内核应用于不同协议或应用程序的允许带宽(最大吞吐量)。
– IP服务质量(QoS):网络流量的优先级排序,由网络组件(例如路由器)执行。这可以通过不同方式实现:IP头包含服务类型(ToS)位,包括一个优先级;这些位已经被重新定义,用于新的QoS方案,包括区分服务[RFC 2474]。其他协议层可能实现了其他优先级,以实现相同的目的。
你的网络可能有各种流量,可以分类为低优先级或高优先级。低优先级可能包括备份传输和性能监控流量。高优先级可能是生产服务器与客户端之间的流量。任何资源控制方案都可以用于限制低优先级流量,从而为高优先级流量提供更好的性能。
这些控制的工作方式因实现而异,将在第10.8节“调优”中讨论。
10.5.11 Micro-Benchmarking
有许多用于网络的基准测试工具。它们在调查分布式应用环境的吞吐量问题时特别有用,以确认网络至少可以达到预期的网络吞吐量。如果不能达到预期吞吐量,可以使用网络微基准测试工具来调查网络性能,这通常比应用程序更简单且更容易调试。
在网络调优到期望速度之后,可以重新关注应用程序。可能被测试的典型因素包括:
– 方向:发送或接收
– 协议:TCP或UDP,以及端口
– 线程数
– 缓冲区大小
– 接口MTU大小
更快的网络接口,例如10 Gbit/s,可能需要驱动多个客户端线程以达到最大带宽。一个示例网络微基准测试工具是iperf,在第10.7.1节“iperf”中介绍。
10.6 Analysis
本节介绍了基于Linux和Solaris操作系统的网络性能分析工具。请参阅前一节以了解在使用它们时要遵循的策略。本节中的工具列在表10.4中。

这是一些工具和功能的选择,用于支持第10.5节《方法论》,从系统范围的统计信息开始,然后深入到数据包嗅探和事件跟踪。查看工具文档,包括man手册,以获取其功能的完整参考资料。
10.6.1 netstat
这是一些工具和功能的选择,用于支持第10.5节《方法论》,从系统范围的统计信息开始,然后深入到数据包嗅探和事件跟踪。查看工具文档,包括man手册,以获取其功能的完整参考资料。
netstat(8)命令根据所使用的选项报告各种类型的网络统计信息。它类似于一个具有多种不同功能的多工具。这些功能包括以下内容:
– (默认):列出连接的套接字
– -a:列出所有套接字的信息
– -s:网络堆栈统计信息
– -i:网络接口统计信息
– -r:列出路由表
其他选项可以修改输出,包括-n以不将IP地址解析为主机名,以及-v用于提供详细信息的情况下的详细输出。
netstat(8)的输出在不同操作系统之间略有不同。
Linux
以下是netstat(8)接口统计信息的示例:

这些列包括网络接口(Iface)、MTU以及一系列接收(RX-)和发送(TX-)的指标:
– OK:成功传输的数据包
– ERR:数据包错误
– DRP:数据包丢失
– OVR:数据包溢出
数据包丢失和溢出是网络接口饱和的指示,可以与错误一起作为USE方法的一部分进行检查。
可以在-i选项中使用-c连续模式,它会每秒打印这些累积计数器。这提供了计算数据包速率的数据。
以下是netstat(8)网络堆栈统计信息的示例(已截断):

输出列出了各种网络统计信息,主要来自TCP,按其协议分组。幸运的是,其中许多具有长的描述性名称,因此它们的含义可能很明显。不幸的是,输出不一致,并包含拼写错误,这在以编程方式处理此文本时会带来麻烦。一些与性能相关的指标已用粗体突出显示,以显示可用的信息类型。其中许多需要对TCP行为有深入理解,包括近年来引入的新功能和算法。以下是一些要查找的示例指标:
– 转发数据包与接收到的总数据包的高比率:检查服务器是否应该转发(路由)数据包。
– 被动连接打开次数:这可以监视客户端连接方面的负载。
– 重传的段与发送出去的段的高比率:可能显示网络不可靠。这可能是预期的(Internet客户端)。
– 因套接字缓冲区溢出而从接收队列中剪切的数据包:这是网络饱和的迹象,可以通过增加套接字缓冲区来修复——前提是系统资源足够,可以维持应用程序的运行。
部分统计名称中包含有拼写错误。如果其他监控工具是基于相同的输出构建的,简单修复这些错误可能会带来问题。这样的工具最好通过阅读这些统计数据的/proc源文件来提供服务,这些源文件是/proc/net/snmp和/proc/net/netstat。例如:

这些/proc/net/snmp统计信息也用于SNMP管理信息库(MIB),为每个统计量提供了进一步的文档说明。扩展统计信息位于/proc/net/netstat中。
netstat(8)可以使用一个以秒为单位的间隔,以便每个间隔持续打印累积计数器。然后可以对此输出进行后处理,以计算每个计数器的速率。
Solaris
这里是netstat(1M)接口统计的一个示例:

列包括网络接口(名称)、MTU、网络(Net/Dest)、接口地址(Address)以及一系列指标:
– Ipkts:输入数据包(接收)
– Ierrs:输入数据包错误
– Opkts:输出数据包(发送)
– Oerrs:输出数据包错误(例如,迟到的碰撞)
– Collis:数据包碰撞(现在不太可能发生,因为有缓冲交换机)
– Queue:始终为零(硬编码,历史遗留)
如果提供了一个间隔(以秒为单位)作为参数,则输出会随时间总结单个接口的情况。可以使用-I选项指定要显示的接口。
以下是netstat(1M)网络堆栈统计的一个示例(截断):

输出按协议分组列出了各种网络统计信息。许多这些统计信息的名称都基于SNMP网络MIB,这些MIB解释了它们的用途。一些与性能相关的指标已经以粗体突出显示,以展示可用的信息类型。其中许多需要对现代TCP行为有深入的理解。要查找的指标包括与之前提到的Linux指标类似的指标,以及:
– tcpListenDrop和tcpListenDropQ0:它们分别显示套接字监听队列和SYN队列中丢弃的数据包数量。tcpListenDrops的增加表示应用程序无法接受的连接请求越来越多。可以通过两种方式来解决这个问题:增加监听队列的长度(tcp_conn_req_max_q),允许更大的连接突发队列;和/或为应用程序配置更大的系统资源。
报告的指标是从kstat中读取的,可以使用libkstat接口访问。
还可以提供一个间隔,它打印自启动以来的摘要,然后是间隔摘要。每个摘要显示该间隔的统计信息(不像Linux版本那样),因此速率是明显的。例如:

这显示了每秒的TCP连接速率,包括活动连接和被动连接。
10.6.2 sar
系统活动报告工具sar(1)可用于观察当前活动,并可配置以存档和报告历史统计信息。它在第4章“可观察性工具”中介绍,并在其他章节中适当提及。Linux版本通过以下选项提供网络统计信息:
– -n DEV:网络接口统计信息
– -n EDEV:网络接口错误
– -n IP:IP数据报统计信息
– -n EIP:IP错误统计信息
– -n TCP:TCP统计信息
– -n ETCP:TCP错误统计信息
– -n SOCK:套接字使用情况
提供的统计信息包括表10.5中显示的内容。

许多统计名称包括方向和所测量的单位:例如,rx表示“接收”,i表示“输入”,seg表示“段”等。请参阅man页面获取完整列表,其中包括ICMP、UDP、NFS和IPv6的统计信息,还注明了一些等效的SNMP名称(例如,ipInReceives对应irec/s)。以下示例每秒打印一次TCP统计信息:

输出显示了每秒大约30个的被动连接率(入站)。网络接口统计列(NET)列出了所有接口;然而,通常只有一个是感兴趣的。以下示例使用了一点awk(1)来过滤输出:

这显示了传输和接收的网络吞吐量。在这种情况下,两个方向的速率都超过了2兆字节/秒。
Solaris版本的sar(1)目前不提供网络统计信息(可以使用netstat(1M)、nicstat(1)和dladm(1M))。
10.6.3 ifconfig
ifconfig(8)命令允许手动配置网络接口。它还可以列出所有接口的当前配置,这在静态性能调整期间很有用,可以检查系统、网络和路由的配置情况。Linux版本的输出包含统计信息。

这些计数器与先前的netstat -i命令描述的相同。txqueuelen是接口传输队列的长度。调整此值的方法在man页面中有描述:
对于传输速度较慢且延迟较高的设备(例如调制解调器链接、ISDN),将此值设置为较小值是有用的,以防止快速大量传输过程对交互式流量(如telnet)造成过多干扰。
在Linux上,ifconfig(8)现在被认为已过时,被ip(8)命令取代。在Solaris上,ifconfig(1M)的各种功能也已经过时,被ipadm(1M)和dladm(1M)命令取代。
10.6.4 ip
Linux的ip(8)命令可用于配置网络接口和路由,以及观察它们的状态和统计信息。例如,显示链接统计信息:

这些计数器与先前描述的netstat -i命令相同,额外增加了接收(RX)和发送(TX)字节。这将允许轻松观察吞吐量;然而,ip(8)目前没有提供打印每个间隔报告的方法(请使用sar(1))。
10.6.5 nicstat
最初为基于Solaris的系统编写的开源工具nicstat(1)可打印网络接口统计信息,包括吞吐量和利用率。nicstat(1)遵循传统资源统计工具iostat(1M)和mpstat(1M)的风格。已经编写了C和Perl版本,适用于基于Solaris的系统和Linux [3]。
例如,以下是在Linux上版本1.92的输出:

第一个输出是自启动以来的总结,接着是间隔摘要。间隔摘要显示eth4接口的利用率为35%(这报告了来自RX或TX方向的最高当前利用率),读取速度为42兆字节/秒。
字段包括接口名称(Int)、最大利用率(%Util)、反映接口饱和统计的值(Sat),以及一系列以r表示“读取”(接收)和w表示“写入”(发送)为前缀的统计信息:
– KB/s:每秒字节数
– Pk/s:每秒数据包数
– Avs/s:平均数据包大小,字节
此版本支持各种选项,包括使用-z跳过零行(空闲接口)和-t用于TCP统计。
nicstat(1)对于使用USE方法特别有用,因为它提供了利用率和饱和度值。
10.6.6 dladm
在基于Solaris的系统上,dladm(1M)命令可以提供接口统计信息,包括数据包和字节速率、错误率和利用率,并且还可以显示物理接口的状态。
每秒显示ixgbe0接口上的网络流量:

输出的第一行是自启动以来的总和,接着是每秒摘要(-i 1)。输出显示该接口目前的接收和发送速率约为500千字节/秒。dladm show-link -S提供了另一种输出,显示了千字节速率、数据包速率和%Util列。
列出物理接口的状态:

这对于静态性能调优非常有用,可以检查接口是否已协商到最快速度。
在dladm(1M)之前,这些属性是使用ndd(1M)来检查的。
10.6.7 ping
ping(8)命令通过发送ICMP回显请求数据包来测试网络连接。例如:

输出包括每个数据包的往返时间(rtt),并显示各种统计摘要。由于时间戳是由ping(8)命令本身测量的,它们包含了在获取时间戳和执行网络I/O之间的一些CPU代码路径执行时间。
Solaris版本需要使用-s选项以这种方式发送连续的数据包。路由器可能会将使用的ICMP数据包视为比应用程序协议更低优先级的数据包,并且延迟可能显示比通常更高的变化。
10.6.8 traceroute
traceroute(8)命令发送一系列测试数据包,实验性地确定到主机的当前路由。这是通过每个数据包增加IP协议的生存时间(TTL)一个来完成的,导致路由到主机的网关序列通过发送ICMP生存时间过期响应消息来显露自己(前提是防火墙没有阻止它们)。
例如,测试加利福尼亚州主机与弗吉尼亚州目标之间的当前路由:

每个跳跃都显示了一系列三个RTT,可以用作粗略的网络延迟统计来源。与ping(8)一样,使用的数据包是低优先级的,可能比其他应用程序协议显示出更高的延迟。
路径也可以作为静态性能调优的一部分进行研究。网络被设计成动态的,并对故障做出响应。随着路径的改变,性能可能已经下降。
traceroute(8)最初由Van Jacobson编写。他后来创建了一个令人惊叹的工具称为pathchar。
10.6.9 pathchar
pathchar类似于traceroute(8),但包括跳跃之间的带宽[4]。这是通过多次发送一系列网络数据包大小并进行统计分析来确定的。以下是示例输出:

不幸的是,pathchar在某种程度上并没有变得流行(也许是因为据我所知,源代码没有发布),而且很难找到适用于现代操作系统的工作版本。它也非常耗时,根据跳数不同可能需要花费几十分钟来运行,尽管已经提出了减少此时间的方法[Downey 99]。
10.6.10 tcpdump
可以使用tcpdump(8)实用程序捕获和检查网络数据包。这可以在STDOUT上打印数据包摘要,也可以将数据包写入文件以供以后分析。后者通常更实用:数据包速率可能太高,无法实时跟踪它们的摘要。
将来自eth4接口的数据包转储到/tmp目录中的文件中:

输出记录了内核放弃传递给tcpdump(8)的数据包数量,这种情况发生在数据包速率过高时。
从转储文件中检查数据包:

输出的每一行显示了数据包的时间(以微秒为分辨率)、源和目标IP地址以及TCP头的值。通过研究这些内容,可以详细了解TCP的运作,包括高级功能如何为您的工作负载提供服务。
使用了-n选项来不将IP地址解析为主机名。还有各种其他选项可用,包括在可用时打印详细信息(-v)、链路层头部(-e)和十六进制地址转储(-x或-X)。例如:

在性能分析过程中,将时间戳列更改为显示数据包之间的增量时间(-ttt)或自第一个数据包以来的经过时间(-ttttt)可能很有用。
还可以提供一个表达式来描述如何过滤数据包(请参阅pcapfilter(7)),以便集中在感兴趣的数据包上。这在内核中执行以提高效率(除了Linux 2.0及更早版本)。
数据包捕获在CPU成本和存储方面都很昂贵。如果可能的话,尽量仅在短时间内使用tcpdump(8)以限制性能成本。
如果有理由不使用snoop(1M)实用程序,可以将tcpdump(8)添加到基于Solaris的系统中。
10.6.11 snoop
虽然tcpdump(8)已经移植到基于Solaris的系统中,但用于数据包捕获和检查的默认工具是snoop(1M)。它的行为类似于tcpdump(8),也可以创建数据包捕获文件以供以后检查。使用snoop(1M),数据包捕获文件遵循[RFC 1761]标准。
例如,在ixgbe0接口上捕获数据包并将其写入/tmp中的文件:

输出包括到目前为止接收到的数据包。使用安静模式(-q)可以将此内容抑制,以便在通过网络会话执行时不会导致额外的网络数据包。
检查来自转储文件的数据包:

输出每个数据包包含一行,以数据包ID编号开头,接着是时间戳(以秒为单位,微秒分辨率)、源和目标IP地址,以及其他协议细节。使用-r选项禁用了将IP地址解析为主机名。
对于性能调查,可以根据需要修改时间戳。默认情况下,它们是增量时间戳,显示数据包之间的时间。-ta选项打印绝对时间:挂钟时间。-tr选项打印相对时间:与第一个数据包的时间差。
-V选项打印半详细输出,包括每个协议栈层的一行:

小写的 -v 选项打印完整详细输出,通常为每个数据包产生一页的输出:

此示例仅包含了第一个数据包。snoop(1M)已经编程实现了解析多种协议的功能,可以快速进行命令行调查以处理各种网络流量。
还可以提供表达式来描述如何过滤数据包(请参阅snoop(1M)的man页面),以便专注于感兴趣的数据包。尽可能地,过滤是在内核中执行以提高效率。
请注意,默认情况下,snoop(1M)捕获整个数据包,包括所有有效负载数据。可以使用-s选项设置截取长度来在捕获时截断。许多版本的tcpdump(8)默认会截断。
10.6.12 Wireshark
虽然tcpdump(8)和snoop(1M)在日常调查中表现良好,但对于深入分析来说,在命令行中使用它们可能会耗费大量时间。Wireshark工具(以前称为Ethereal)提供了一个图形界面,用于数据包捕获和检查,并且还可以从tcpdump(8)或snoop(1M)导入数据包转储文件。其有用的功能包括识别网络连接及其相关数据包,以便可以分开研究,还可以翻译数百种协议头。
10.6.13 DTrace
DTrace可以用于检查内核和应用程序中的网络事件,包括套接字连接、套接字I/O、TCP事件、数据包传输、积压丢弃、TCP重传以及其他细节。这些功能支持工作负载特征化和延迟分析。
以下部分介绍了用于网络分析的DTrace,演示了适用于Linux和Solaris系统的功能。其中许多示例来自基于Solaris系统,也包括一些来自Linux。在第4章“可观测性工具”中包含了DTrace入门。
用于跟踪网络栈的DTrace提供程序包括表10.6中列出的那些。

最好使用稳定的提供程序,但它们可能尚未在您的操作系统和DTrace版本中可用。如果没有,可以使用不稳定的接口提供程序,尽管脚本需要更新以匹配软件更改。
套接字连接
套接字活动可以通过执行网络操作的应用程序函数、系统套接字库、系统调用层或内核进行跟踪。系统调用层通常是首选,因为它有良好的文档、低开销(基于内核)且适用于整个系统。通过connect()计算出站连接的数量:

这个一行命令用于统计connect()系统调用的次数。在这种情况下,调用connect()最多的是名为haproxy的进程,它调用了22次connect()。如果需要,输出中可以包括其他详细信息,包括PID、进程参数和connect()参数。
通过accept()计算入站连接的数量:

在这种情况下,名为node的进程接受了最多的连接,总共有24个。
在套接字事件期间,可以检查内核和用户级堆栈,以显示执行它们的原因,作为工作负载特征化的一部分。例如,以下跟踪连接()用户级堆栈的进程名为ssh:

这些系统调用的参数也可以被检查。这需要DTrace比平常更多的工作,因为有趣的信息位于一个结构体中,必须从用户空间复制到内核空间,然后进行解引用。这由soconnect.d脚本执行(来自[Gregg 11])。

以下是示例输出:

这跟踪connect()系统调用,并打印一行输出来总结它们。系统调用的延迟包括在内,并且系统调用返回的错误代码(errno)被转换为字符串。错误代码通常是“进行中”,这发生在非阻塞连接()中。
除了connect()和accept()之外,还可以跟踪socket()和close()系统调用。这允许在创建时查看文件描述符(FD),并通过时间差来测量套接字的持续时间。
套接字I/O
在建立套接字之后,可以基于文件描述符在系统调用层跟踪后续的读取和写入事件。这可以通过以下两种方式之一来执行:
关联数组的套接字FD:这涉及跟踪syscall::socket:return并构建一个关联数组,例如,is_socket[pid,arg1] = 1;。在谓词中可以检查数组以识别将来的I/O系统调用中的哪些FD是套接字。记得在syscall::close:entry上清除值。
如果在您的DTrace版本中可用,则使用fds[].fi_fs的状态。这是文件系统类型的文本字符串描述。由于套接字映射到VFS,它们的I/O与虚拟套接字文件系统关联。
以下一行命令使用了后一种方法。
通过execname计算通过read()或recv()读取的套接字数:

这个输出显示,在跟踪过程中,名为 node 的进程使用这些系统调用中的任何一个从套接字读取了 1,218 次。
通过 execname 计算通过 write() 或 send() 写入套接字的次数,是以下一行命令:

请注意,您的操作系统可能会使用这些系统调用的变体(例如,readv()),这些变体也应该被跟踪。
I/O 的大小也可以通过跟踪每个系统调用的返回探测来检查。
套接字延迟
鉴于可以在系统调用层跟踪套接字事件,以下测量可以作为延迟分析的一部分进行:
连接延迟:对于同步系统调用,连接(connect())的时间。对于非阻塞 I/O,从发出 connect() 到 poll() 或 select() (或其他系统调用) 报告套接字准备就绪的时间。
首字节延迟:从发出 connect() 或 accept() 返回的时间,到通过该套接字的任何 I/O 系统调用接收到第一个数据字节的时间。
套接字持续时间:从相同文件描述符的 socket() 到 close() 的时间。为了更专注于连接持续时间,可以从 connect() 或 accept() 计时。
这些可以作为长一行命令或脚本来执行。它们也可以从其他网络堆栈层次执行,包括 TCP。
套接字内部
可以使用 fbt 提供程序跟踪套接字的内部内核。例如,在 Linux 上,列出以 sock_ 开头的函数:

输出已被截断——它列出了超过 100 个探测点。可以单独跟踪每一个探测点,以及其参数和时间戳,以回答关于套接字行为的任意问题。
TCP 事件
与套接字类似,可以使用 fbt 提供程序跟踪 TCP 的内部内核。但是,已经开发了一个稳定的 tcp 提供程序(最初由我开发),并且可能已经在您的系统上可用。这些探测点如表 10.7 所示。

其中大多数提供了显示协议头详细信息和内部内核状态的参数,包括“缓存”的进程ID。通常使用 DTrace 内置的 execname 跟踪的进程名称可能无效,因为内核 TCP 事件可能会异步发生于进程。频率统计接受的 TCP 连接(被动)与远程 IP 地址和本地端口:

在跟踪过程中,主机 10.2.204.30 连接到 TCP 本地端口 636 五次。类似的延迟可以使用 TCP 探针进行跟踪,如前面套接字延迟部分所述,使用 TCP 探针的组合。
列出 TCP 探针:

MODULE 和 FUNCTION 字段显示了探测点在内核代码中的(不稳定)位置,可以使用 fbt 提供程序跟踪以获取更多详细信息。
数据包传输
要研究 TCP 提供程序以外的内核内部情况,以及当 TCP 提供程序不可用时,可以使用 fbt 提供程序。这是动态跟踪使某些事情成为可能的情况之一——这比不可能要好,但不一定容易!网络堆栈的内部结构很复杂,初学者可能需要花费多天的时间来熟悉代码路径。
浏览堆栈的快速方法是跟踪一个深度事件,然后检查其堆栈回溯。例如,在 Linux 上,跟踪 ip_output() 并使用堆栈:

每行标识了一个可以单独跟踪的内核函数。这需要检查源代码以确定每个函数及其参数的作用。
例如,考虑到 tcp_sendmsg() 的第四个参数是以字节为单位的大小,可以使用跟踪它:

这个一行命令使用 quantize() 动作将 TCP 发送段的大小总结为二的幂分布图。大多数段的大小在 128 到 511 字节之间。
可以编写更长的一行命令和复杂的脚本,比如用于调查 TCP 重传和积压丢弃的脚本。
重传跟踪
研究 TCP 重传对于调查网络健康状态是一项有用的活动。虽然以往通常通过使用嗅探工具将所有数据包转储到文件中进行事后检查来执行此操作,但是 DTrace 可以实时检查重传,并且开销很低。以下脚本适用于 Linux 3.2.6 内核,跟踪 tcp_retransmit_skb() 函数并打印有用的细节:

这包括时间、目标 IP 地址和内核堆栈跟踪,有助于解释为什么发生了重传。为了获得更详细的信息,可以单独跟踪内核堆栈中的每个函数。
类似的脚本已经为 SmartOS 开发,作为云运营商工具包的一部分。其中包括 tcpretranssnoop.d,其输出如下:

这显示了 TCP 重传的目标 IP 地址(在此输出中已编辑),并包括内核 TCP 状态。
积压丢弃
这个最后的示例脚本也来自 SmartOS 的 TCP 脚本工具包,用于估算是否需要进行积压调优以及其是否有效。这是一个较长的脚本,提供为高级分析的示例。

该脚本同时使用不稳定的 fbt 提供程序获取 TCP 状态,并使用 mib 提供程序计算发生丢弃的次数。
以下是示例输出:

当按下 Ctrl-C 键时,将打印一个摘要,显示缓存的进程 ID(cpid)、套接字积压的当前最大长度(max_q),以及一个分布图,显示在添加新连接时测量的积压长度。输出显示,PID 11504 发生了 34 次积压丢弃,最大积压长度为 128。分布图显示,大部分时间积压长度为 0,只有一小部分将队列推到了最大值。这是增加队列长度的候选方案。
通常只有在发生丢弃时才调整这个积压队列,可以通过 netstat -s 命令的 tcpListenDrops 计数器看到这一点。这个 DTrace 脚本允许在丢弃成为问题之前预测丢弃并应用调整。以下是另一个示例输出:

在这种情况下,积压通常达到了其 128 的限制。这表明应用程序负载过重,没有足够的资源(通常是 CPU)来跟上。
更多追踪
在需要时,动态追踪可以以其他方式和更详细的方式探索网络。为了提供对其功能的一个概念,表10.8 显示了来自 DTrace 的《网络底层协议》章节(158 页)中的脚本[Gregg 11]。这些脚本也可以在线查阅 [7]。

DTrace 书中还有一个关于应用层协议的章节,提供了许多用于跟踪 NFS、CIFS、HTTP、DNS、FTP、iSCSI、FC、SSH、NIS 和 LDAP 的更多脚本。尽管这种可观测性的程度令人难以置信,但其中一些动态跟踪脚本与特定的内核内部绑定在一起,需要进行维护以适应较新内核版本中的更改。另一些基于特定的 DTrace 提供程序,这些提供程序可能尚未在您的操作系统上可用。
10.6.14 SystemTap
SystemTap 也可以在 Linux 系统上用于动态跟踪文件系统事件。有关将先前的 DTrace 脚本转换的帮助,请参阅第 4 章“可观测性工具”的第 4.4 节“SystemTap”,以及附录 E。
10.6.15 perf
在第6章“CPU”中介绍的LPE工具集也可以提供一些网络事件的静态和动态跟踪。它可以用于识别在内核中导致网络活动的堆栈跟踪,就像以前使用DTrace进行数据包传输和重传跟踪一样。更高级的工具也可以使用后处理来开发。
例如,以下示例使用perf(1)为tcp_sendmsg()内核函数创建一个动态跟踪点,然后在5秒内跟踪它以及调用图(堆栈跟踪):

输出显示了导致内核调用tcp_sendmsg()发送数据的sshd的堆栈跟踪,以便通过TCP连接发送数据。
还有一些预定义的用于网络的跟踪点事件:

skb跟踪点用于套接字缓冲区事件,net用于网络设备。
这些也可以用于网络调查。
10.6.16 Other Tools
其他 Linux 网络性能工具包括:
– strace(1):用于跟踪与套接字相关的系统调用并检查所使用的选项(注意,strace(1)的开销较大)。
– lsof(8):按进程ID列出打开的文件,包括套接字详细信息。
– ss(8):套接字统计信息。
– nfsstat(8):NFS 服务器和客户端统计信息。
– iftop(8):按主机汇总网络接口吞吐量(嗅探器)。
– /proc/net:包含许多网络统计文件。
对于 Solaris:
– truss(1):用于跟踪与套接字相关的系统调用并检查所使用的选项(注意,truss(1)的开销较大)。
– pfiles(1):用于检查进程正在使用的套接字,包括选项和套接字缓冲区大小。
– routeadm(1M):用于检查路由和 IP 转发的状态。
– nfsstat(1M):NFS 服务器和客户端统计信息。
– kstat:提供来自网络堆栈和网络设备驱动程序的更多统计信息(其中许多在源代码之外未记录)。
还有许多网络监控解决方案,要么基于 SNMP,要么运行其自己的定制代理。
10.7 Experimentation
除了ping(8)、traceroute(8)和之前介绍过的pathchar之外,用于网络性能分析的其他实验性工具包括微基准测试。这些工具可以用于确定主机之间的最大吞吐量,有助于在调试应用程序性能问题时确定端到端网络吞吐量是否存在问题。
可以选择许多网络微基准测试工具。本节演示了流行且易于使用的iperf。另一个值得一提的是netperf,它也可以测试请求/响应性能。
10.7.1 iperf
iperf是一个用于测试最大TCP和UDP吞吐量的开源工具。它支持各种选项,包括并行模式:可以使用多个客户端线程,这可能对将网络推向极限是必要的。iperf必须在服务器和客户端上都执行。
例如,在服务器上执行iperf:

这将套接字缓冲区大小增加到128 KB(-l 128k),默认值为8 KB。
在客户端上执行以下操作:

这使用了以下选项:
-c host:连接到主机名或IP地址
-l 128k:使用128 KB套接字缓冲区
-P 2:以两个客户端线程并行模式运行
-i 1:每秒打印间隔摘要
-t 60:测试的总持续时间:60秒
最后一行显示了测试期间的平均吞吐量,总和计算了所有并行线程:244 Mbits/s。
可以检查每个间隔摘要以查看随时间的变化。–reportstyle C选项可用于输出CSV格式,以便其他工具(如绘图软件)导入数据。
10.8 Tuning
网络可调参数通常已经调优,以提供高性能。网络栈通常也设计成能够动态响应不同的工作负载,从而提供最佳性能。
在尝试调整可调参数之前,首先了解网络使用情况是值得的。这可能还会识别出可以消除的不必要工作,从而带来更大的性能提升。尝试使用前一节中介绍的工具进行工作负载特性化和静态性能调优方法。
可用的可调参数在操作系统的不同版本之间会有所不同。请参阅它们的文档。接下来的各节提供了可能可用的内容以及它们如何进行调整的想法;它们应被视为根据您的工作负载和环境进行修订的起点。
10.8.1 Linux
可调参数可以使用sysctl(8)命令进行查看和设置,并写入/etc/sysctl.conf。它们也可以从/proc文件系统的/proc/sys/net目录下进行读取和写入。
例如,要查看当前TCP可用的参数,可以从sysctl(8)中搜索包含文本tcp的参数:

在这个内核(3.2.6-3)中,有63个包含tcp的参数,以及更多位于net目录下的参数,包括IP、以太网、路由和网络接口的参数。
具体调优的例子将在以下各节中进行介绍。
套接字和TCP缓冲区
所有协议类型的最大套接字缓冲区大小,包括读取(rmem_max)和写入(wmem_max),可以通过以下方式进行设置:

该值以字节为单位。为了支持全速的10 GbE连接,可能需要将其设置为16兆字节或更高。
启用TCP接收缓冲区的自动调优:

设置TCP读取和写入缓冲区的自动调优参数:

每个参数都有三个值:使用的字节数的最小值、默认值和最大值。使用的大小是根据默认值自动调整的。为了提高TCP吞吐量,尝试增加最大值。增加最小值和默认值将会消耗更多的内存每个连接,这可能是不必要的。
TCP队列
第一个队列是用于半开放连接的backlog队列:

第二个队列是用于传递连接到accept()的监听backlog队列:

这两个队列的大小可能需要从它们的默认值增加,例如,增加到4,096和1,024,或更高,以更好地处理负载突发情况。
设备队列
增加每个CPU的网络设备backlog队列的长度:

这可能需要增加,例如,对于10 GbE网卡,可以增加到10,000。
TCP拥塞控制
Linux支持可插拔的拥塞控制算法。列出当前可用的算法:

有些可能是可用的,但当前没有加载。例如,添加htcp算法:

当前的算法可以使用以下方式进行选择:

TCP选项
其他可以设置的TCP参数包括

SACK和FACK扩展,它们可以在高延迟网络上提高吞吐性能,但会增加一些CPU成本。
tcp_tw_reuse参数允许在看起来安全时重用TIME-WAIT会话。这可以允许两个主机之间的连接速率更高,例如在Web服务器和数据库之间,而不会因为TIME-WAIT状态的会话达到16位临时端口限制。
tcp_tw_recycle是另一种重用TIME-WAIT会话的方法,虽然不像tcp_tw_reuse那样安全。
网络接口
可以使用ifconfig(8)来增加TX队列长度,例如:

这对于10 GbE网卡可能是必要的。该设置可以添加到/etc/rc.local中,以便在启动时应用。
资源控制
容器组(cgroups)网络优先级(net_prio)子系统可用于为出站网络流量的进程或进程组应用优先级。
这可以用于优先处理高优先级的网络流量,例如生产负载,而不是低优先级的流量,例如备份或监控。配置的优先级值被转换为IP ToS级别(或使用相同位的更新方案)并包含在数据包中。
10.8.3 Configuration
除了可调参数之外,以下配置选项也可以用于调整网络性能:
以太网巨帧:将默认的MTU从1,500增加到约9,000可以提高网络吞吐量性能,如果网络基础设施支持巨帧。
链路聚合:多个网络接口可以组合在一起,以使它们作为一个单一的接口,并具有组合的带宽。这需要交换机的支持和配置才能正常工作。
套接字选项:应用程序可以使用setsockopt()来调整缓冲区大小,增加它(最多达到先前描述的系统限制)以提高吞吐量性能。这些对于两种操作系统类型都是通用的。
#以上关于10 Network的相关内容来源网络仅供参考,相关信息请以官方公告为准!

原创文章,作者:CSDN,如若转载,请注明出处:https://www.sudun.com/ask/91523.html

(0)
CSDN's avatarCSDN
上一篇 2024年6月22日 下午2:12
下一篇 2024年6月22日 下午2:30

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注