云服务器价格_云数据库_云主机【优惠】最新活动-搜集站云资讯

轻量服务器_大带宽云主机_促销

小七 141 0

监听插座的复仇

去年11月,我们写了一篇关于一个延迟峰值的博客文章。今天我想和大家分享这个故事的续篇。事实证明,配置错误的rmem设置并不是增加延迟的唯一原因。看来沃尔夫先生还没有完成他的工作。在调整了前面讨论的rmem sysctl之后,我们继续监视系统的延迟。除此之外,我们还测量了边缘服务器的ping时间。虽然最坏的情况有所改善,我们再也看不到1000ms+的ping了,但线路仍然不平坦。下面是空闲的内部计算机和生产服务器之间ping延迟的图表。测试是在数据中心内进行的,数据包从未进入公共互联网。图表的Y轴以毫秒为单位显示ping时间,X轴是测量的时间。每秒钟进行6小时以上的测量:如你所见,大多数ping都是在1ms以下完成的,但是在21600次测量中,大约有20次的延迟高达100ms,不是很理想吗?系统分接头延迟发生在我们的数据中心内,数据包没有丢失。这又暗示了一个内核问题。Linux从它的软中断处理代码响应ICMP ping。处理ping的延迟表示软IRQ处理中的延迟,这是非常糟糕的,可能会影响传递到机器的所有数据包。使用系统tap脚本,我们能够测量主软IRQ函数net_rx_action的时间分布:这种分配相当糟糕。虽然大多数对net_rx_action的调用都在81us以下(平均值),但缓慢的异常值非常糟糕。三通电话花了32毫秒!难怪平时代取消了。inet_查找速度慢有一些来回的火焰图和直方图-内核.stp我们深入寻找罪魁祸首。我们发现,tcp_v4_rcv的延迟分布也很差。更具体地说,问题出现在tcp_ipv4.c文件中tcp_v4_rcv函数的第1637行和第1642行之间。我们写了另一个脚本来说明:延迟在tcp_v4_rcv函数中的特定行创建:sk=\uu inet_lookup_skb(&tcp\u hashinfo,skb,th->source,th->dest);上面显示的数字表明该函数通常很快终止,在2us以下,但有时它会遇到一个缓慢的路径,需要1-2ms才能完成。_uinet_lookup_skb是内联的,这使得精确测量变得困难。幸运的是,这个函数很简单-它所做的只是调用u inet_lookup_established和\uu inet_lookup_listener。正是后者造成了麻烦:让我们来讨论一下如何使用"inet"查找。此函数尝试为数据包找到适当的连接套接字结构。这是在已建立的调用中完成的。如果失败,\uu inet_查找将尝试查找处于侦听状态的绑定套接字,该套接字可能会处理数据包。例如,如果数据包是SYN并且侦听套接字存在,我们应该用SYN+ACK响应。如果没有绑定的监听套接字,我们应该发送一个RST。unu inet_lookup_listener函数在LHTABLE哈希表中查找绑定的套接字。它使用目标端口作为散列,并在哈希表中选择一个适当的bucket。然后它线性地迭代它以找到匹配的侦听套接字。为了理解这个问题,我们使用另一个特制的系统tap脚本跟踪了慢包。它挂接在u inet_lookup_listener上,并只打印出慢速数据包的详细信息:有了这些数据,我们更深入地将这些日志行与tcpdump捕获的特定数据包相匹配。我不告诉你细节,但这些是入站SYN和RST包,目的端口模32等于21。过来看:16725%32=2153%32=2163925%32=21现在,这个神奇的数字从哪里来?监听哈希表如上所述,Linux维护一个包含侦听TCP套接字的侦听哈希表—LHTABLE。它有32个固定大小的桶:/*是的,真的,这就是你所需要的。*/#定义INET_LHTABLE_大小32回顾一下:所有SYN和RST包都会触发LHTABLE中的查找。由于连接项不存在,\uu inet_lookup_建立的调用失败,将调用u inet_lookup_侦听器。LHTABLE很小,只有32个桶。LHTABLE仅按目标端口散列。是时候快速转移注意力了。让我们来谈谈CloudFlare的DNS服务器。让我们来谈谈DNS在CloudFlare,我们使用一个名为rrdns的自定义DNS服务器。在许多其他要求中,服务器被设计为能够抵御DDoS攻击。尽管我们的服务器速度很快,但当一个大型攻击开始时,它可能无法应对攻击负载。如果发生这种情况,我们必须控制损害-即使一个IP地址受到严重攻击,服务器仍必须处理其他IP地址上的合法流量。事实上,我们的DNS架构是为了在16k个IP地址之间分配负载而设计的。当IP地址受到攻击,并且服务器无法跟上传入的数据包时,UDP套接字上的内核接收队列将溢出。我们通过查看netstat计数器来监视:$netstat-s—udpUdp协议:4395775包接收错误随着该数字的增加,我们可以通过列出具有非空接收队列的UDP套接字来查看受影响的IP地址:$netstat-ep4ln--udp | grep 53 | egrep-v"^udp*0"udp 524293 0 173.245.1.1:53 0.0.0.0:*0udp 524288 0 173.245.2.3:53 0.0.0.0:*0在这种情况下,两个IP地址接收到大量的UDP通信。这超出了DNS服务器的处理能力,接收队列不断累积,最终溢出。幸运的是,由于我们绑定到特定的IP地址,溢出一些UDP接收队列不会影响任何其他IP地址。绑定到特定的IP地址是保持DNS基础设施在线的关键。使用此设置,即使其他缓解技术失败,并且DNS服务器暴露在数据包洪水中,我们确信该攻击不会影响在其他IP地址上处理DNS。但那和LHTABLE有什么关系?在我们的设置中,我们绑定到UDP和TCP的特定IP地址。虽然在UDP中有16k个侦听套接字是可以的,但事实证明它对于TCP来说并不合适。怎么搞的?由于我们的DNS设置,我们有16k个TCP套接字绑定到端口53上的不同IP地址。由于端口号是固定的,所有这些套接字都在一个LHTABLE bucket中结束。21号桶(这是53号桶)。当RST或SYN数据包命中时,\uu inet_lookup_listener调用必须遍历所有16k个套接字条目。这并不快,事实上花了2毫秒才完成。为了解决这个问题,我们部署了两个更改:对于TCP连接,我们的DNS服务器现在绑定到任何一个\u IP地址(aka:0.0.0.0:53,*:53)。我们称之为"绑定到星星"。虽然UDP仍然需要绑定到特定的IP地址,但是这样做对TCP流量没有什么好处。对于TCP,我们可以安全地绑定到star,而不会损害我们的DDoS防御。我们增加了内核的大小。我们不是第一个这样做的人:谷歌的比尔·索默菲尔德早在2011年就提出了这个建议。部署了这些更改后,我们数据中心内的ping时间最终变平了,因为它们应该一直是:最后的话你不能有大量绑定的TCP套接字,我们很难理解这一点。我们了解了一些关于Linux网络栈的知识:LHTABLE是固定大小的,并且仅按目标端口散列。我们再次展示了几个强大的系统Tap脚本。随着补丁的部署,最大延迟数显著下降。我们确信,在网络操作中的软中断处理性能良好。沃尔夫先生终于完成了他的任务。如果这听起来很有趣,考虑加入我们。我们在新加坡、旧金山和伦敦都有团队。