TCP/IP 协议栈调试

虽然我们大家对TCP/IP很熟悉,但是很少有人正真地了解它是如何工作的,更不用说如何合理地对其进行调试或故障排除。TCP/IP是核心的网络协议,整个互联网以及每个使用互联网的人每天都在用这些协议。这些协议引导着数据如何在网络上移动,自30年前互联网移动到TCP/IP协议之后,它一直都工作的很好。

1977年有一个很有趣的实验,Vint Cerf 和他的团队在硅谷高速公路上使用无线电天线车测试并调试TCP。他们在海湾两岸来回发送数据,直到警察感到好奇并阻止了他们。以下是其中一辆车:

http://en.wikipedia.org/wiki/File:SRI_Packet_Radio_Van.jpg  如果你对网络和TCP确实很感兴趣的话,你也可以查看如何通过鸟类来传输数据,请参考RFC 1149:http://en.wikipedia.org/wiki/IP_over_Avian_Carriers

辛运的是,TCP/IP配置和调试在此后取得了长足进展,自90年代后,我们有了Windows/WFW winsock.dll,Trumpet 或 TiA 或在9600波特上通过SLIP进行连接。现在TCP/IP已经融入万物,无所不在,甚至是面包师和咖啡师也使用。甚至出现了TP-Planet 协议,用于星球之间的通讯,如火星和其它星球之间的宇宙飞船之间的通讯。

回到我们21世纪的地球上,我们仍旧在使用低延迟甚至是高延迟的网络在通讯。更不必说,会有很高网络性能要求的系统的出现,这就给我们足够的理由要求我们自我检查我们的网络并对其进行调试。

首先要检查的是时间戳、大型窗口(window)尺寸和SCK。每个TCP/IP学生都知道窗口以及如何管理窗口尺寸,但是,更重要的是,要明白窗口是如何影响流量的,这是我们的高性能目标。现今,我们需要的窗口要大于原先的64KB规格。幸运的是,我们可以进行选择,而且这个选择在默认情况下是处于开启状态的。

时间戳也很重要,可以更好地管理好32位序列号,更好地计算数据包往返时间(RTT),这样可以优化窗口尺寸。选择性确认(SACK)也是很重要的一项,它可以帮我们减少数据包丢失时候的重传现象,尤其是当网速较快、窗口较大并且有带宽时延积的时候,更能够减少数据包重传。幸运的是,许多系统的默认配置都支持这些,所以,如果不是意味关闭这些默认设置的话,这些问题将不复存在。看了这些问题之后,我们要想一想还有什么其它事项会影响到我们。

第一个要思考的是,一个服务比如Nginx是如何接受连接的。客户端发送一个SYN给服务器,就是内核接受并转发到服务器的SYN。服务器然后会调用accept(),内核会发生一个SYN/ACK返回给客户端。然而,当很忙的时候,服务器所收到的SYN远比所能接受accept()的SYN要多的多,所以,就会在内核里形成SYN队列。这就是所谓的SYN 积压,这种积压程度是可以进行配置的;如果积压的SYN超过了所设置的限制,内核会丢弃(discard)SYN。客户端会在3秒之后再次尝试发送SYN,9秒之后再次尝试,但是,情况依旧很糟、速度依旧很慢。

在内核,是通过net.ipv4.tcp_max_syn_backlog和net.core.somaxconn来设置的。默认值是1024,但是最大值可以设置为65,535。但是,即便你把这个值设置的很高,服务器也不会使用到这么高,因为服务器必须为第一个listen()打开侦听套接字预留请求空间。许多Nginx、Redis和Apache服务的默认设置为511,对于MySQL甚至可能更低,只有50。 幸运的是,许多这些服务器可以被设置的很高,至少1024可以作为起点,所以,一定要记得设置您的配置文件。

另一个SYN问题是SYN泛滥,这是陈旧的DoS方法造成的,它会使内核SYN架构过载或使其充满SYN。这可以通过SYN Cookies来解决,在Linux中,通常是通过自动模式来进行配置,当SYN积压队列满溢的时候,可以打开这个配置进行调试。这种方法能够很有效地解决问题,但是,当它处于激活状态的时候,它会禁用大型窗口,因为它使用的是同样的头位 (header bits)。

许多人听说过TCP卸载,指的是快速服务器CPU会卸载TCP计算比如NIC卡中的冗余校验和。但是,这种做法有许多问题也很复杂,现在的卡上最好把这些关闭掉。除非是经过测试,在快速系统上确实有好处,比如10Gbps 的1Gbps 小数据包,才把他们开启。

我们继续往下看,现今的TCP/IP最核心的部分是慢速启动,随着连接数运行,窗口大小慢慢增大,每个ACK 增大2倍。这对于长时间的连接和大的数据量传输是有益的,但是如果有许多小型数据连接的话(比如,当没有HTTP持久连接的话),就会有害处。我们不想对此大谈阔论,因为,1986年,当时还没有这个选择方案,互联网由于堵塞而崩溃。这是可调试的,常规缺省值为4个段或大概4KB数据。您应该注意到,有件事情很有趣,就是2.6.39以及以上版本的内核默认值为10个段,但是谷歌研究员建议这个值可以达到16。

但是对于2.6.19 及以上版本的内核,不容易更改设置,也就是说,在RedHat/CentOS 5 中无法更改设置,必须使用“iproute change . . . initcwnd 10”进行更改,而“iproute change . . . initcwnd 10”通常是在rc.local中。此外,您也应该测试“net.ipv4.tcp_slow_start_after_idle = 0”这个设置,如果不测试的话,当有HTTP持久连接存在的话,如果窗口空闲3秒钟的话,窗口会被重新设置。这对于经常被浏览的网站如电商类网站而言是很有好处的。

另一个需要调整的是TIME_WAIT系统,在繁忙系统中,您会看到许多这类东西,默认情况下,它会在关闭后仍旧有120秒的时间处于激活状态,所以,如果1秒钟有1000个连接的话,总时间就会达到120,000秒,我们希望它们可以快速地被清理掉,所以把它设置为‘net.ipv4.tcp_fin_timeout = 15.’

我们还必须关注iptables及其相关的连接跟踪器。在繁忙系统中,它会轻易地消耗掉连接跟踪插槽,连接跟踪插槽的缺省设置是65,635。因为它采用同TCP基本相同的方式进行跟踪,使用的也是120秒,所以,在高负荷时会丢失数据。要提高这个极限,可以把它设置为‘net.nf_conntrack_max’,以便达到1048576。为了更快地清理,可以把它设置为‘net.netfilter.nf_conntrack_tcp_timeout_fin_wait = 15’,与TCP时间延迟相匹配。您也可以设置并发跟踪时间延迟,但是并不是很值得,因为许多都是TIME_WAIT,而您已经提高了TIME_WAIT。

有些人会更改‘net.ipv4.tcp_max_tw_buckets’,把缺省值改为65,535到131,072之间的任何一个数值。这必须同您的‘conntrack_max,’的大小相同,或者至少等于每秒连接数* TIME_WAIT时间延迟。到目前为止是2000/秒和120秒,我们需要将其设置为240,000。当然,如果你把延迟降低到15秒,那么原先的默认值就足够了,可以供许多人使用,每秒可以连接5000甚至更多。

高性能的系统可以设置成‘net.ipv4.tcp_tw_reuse = 1’,这样可以重新使用TIME_WAIT静态套接字,这是很好的,但是不要打开‘net.ipv4.tcp_tw_recycle’,因为它会带来许多问题。如果您想了解更多关于这个主题的信息,请查看我们的博客,博客名叫“Linux TCP 回收和重用”。快速系统有时候也可以使用较大的‘net.ipv4.tcp_max_orphans’,这个设置的默认值是65,535-如果您有桶或者dmesg错误的话,您也可以把它设置的更高。

TCP内存和相关的缓冲器也是一个大问题,但是许多系统都能够自动把它调试的很好。但是,您仍旧必须检查您的缺省设置,确保这个缺省设置足够的大,最少要把它设置为‘net.ipv4.tcp_rmem = 4096 65536 16777216’ ,‘net.ipv4.tcp_rmem’也必须设置到相同的值。此外,为了设置TCP rmen和wmen,您也必须设置网络核心。

有些人还推荐其它一些设置项目,但是这些设置对服务器或客户端会产生什么样的影响还不甚清楚。一个例子就是‘net.ipv4.tcp_no_metrics_save = 1’,这个设置可以重设每个连接的慢速启动窗口。其它人推荐去htcp Hamilton TCP 堵塞窗口管理,但是通常内核会小于2.6.33(比如在Redhat/Centos 5/6中)。如果您想要这个设置,把它设置为‘net.ipv4.tcp_congestion_control=htcp’,这样,它便可以自动加载tcp_htcp模块。

请注意,为了改进系统性能,您还可以对其它一些与NIC-相关的项目进行设置,但是许多此种类型的设置都是为了防止NIC过载或彻底丢失数据包,比如irq 绑定和平衡。更重要的是NIC txqueuelen, 可以通过ifconfig进行设置,通常在1Gbps 网络的NIC运行中应当将这个值设置为1000,但是缺省值应该比这个值要小的多。此外,如果使用的是1Gbps 或更高的网络(如10G),应当把‘net.core.netdev_max_backlog’ 设置为2500,这样可以增加空间,以防数据包充满系统。

最后,请确保您有足够数量的全球文件句柄。从fs.file-nr开始,通常缺省设置为1,024,000到几百万都是很好的,但是在2.6.18内核上,至少也可能有1,000,000的限制。

虽然我们每天都学习更多的有关TCP调试的知识,但是这确实是一项挑战。但是,上述建议应当有些小帮助,可以加速所有数据(原文是bits)的传输。


Tags: Linux, Networking, 内核
Categories: Linux

相关文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注

* Copy This Password *

* Type Or Paste Password Here *