小心!TCP本机客户端链接本机服务器

上周,在咱们进行性能测试的时候,发现了一个问题。
咱们的服务器上启了一个redis服务端,侦听0.0.0.0的1234端口,同处在本机的另一个进程会频繁发起到该服务端的短链接,结果致使了两个问题:
1.大量的TIME_WAIT状态的链接;
2.发起链接的进程的CPU占用率接近100%。
这两个结果严重影响了咱们网关的性能,在分析具体缘由以前,首先作一个提倡,那就是:本机链接本机,首选UNIX域套接字而不是TCP!

缘由其实不须要数据分析,仅仅理论分析就够了,前提是你要作Linux内核协议栈的IP层处理以及软中断调度有足够的理解,固然,这些都很简单。
       首先咱们来看看问题1。TIME_WAIT就很少说了,只要任何一端主动断开链接,那么它最终可能将会进入TIME_WAIT状态,具体是否会进入在 Linux上取决于几个因素,第一,你有没有两端开启timestamps,若是开启了,你有没有在服务端开启recycle,若是开启了,那么 TIME_WAIT套接字就会迅速消失,也就是说,想让recycle起做用,你必定要开启timestamps。若是没有timestamps,那么就 会有大量的TIME_WAIT状态的套接字。
       在Linux内核协议栈的实现中,全部链接本机的数据流,其路由选择最终都会到定向到loopback,若是你没有绑定源IP地址,那么源/目标IP地址 均为127.0.0.1!若是服务端口是固定的,那么最终会接受65535-1个链接,减1的缘由在于服务端已经bind了服务端口,所以客户端不能再次 bind。这是合理的,由于按照四元组惟一性考虑,一个服务只能接受一个特定IP地址的65535个链接或者65534个链接,可是问题是,若是需求巨 大,这显然不能知足要求,你要知道,做为服务器而言,它要考虑的是总的最大并发链接数,一台机器上同时发起6万多个链接的可能性并不大,所以TCP在大多 数状况下是合理,采用16bit的端口号刚恰好,由于协议头不能太大,不然载荷率就会变小,这显然是网络传输所要求的,然而本机连本机时,并不须要网络传 输,你想固然会认为有多少需求就要都要知足,不过TCP并不适合这种场合。
       本机连本机,没有网络传输带来的延迟,吞吐限制也仅限于本机资源利用,所以并发10万甚至更多的需求都是合理的,但是TCP并不能知足,缘由就在于它只有 16bit的端口号,目标端口固定,同时只能有65534个链接。如何解决呢?咱们知道127.0.0.0/8都是属于loopback的,咱们能够采用 不一样的源IP地址,若是想这么作,有两个选择,那就是要么客户端bind源IP为127.x.y.z,要么SNAT成127.x.y.z,这样就能够接受 海量的链接需求了。可是这并非最终的解决方案,为何非要用TCP呢?TCP原本就是为网络传输设计的,它的流控应对不一样的主机,拥控应对反复无常的网 络,在本机,这些都不是问题,因此本机连本机,最好使用本机套接字,好比UNIX域套接字。
       再来看问题2,一个链接本机的TCP数据包最终到达了loopback的xmit发送函数,其中简单的调度了本CPU上的一个软中断处理,而后会在下一次 中断结束后调度其执行,这有很大概率是在当前发送进程的上下文中进行的,也就是说,发送进程在其上下文中进行了发送操做,而此时软中断借用了其上下文触发 了接收操做,再而后,LOCK的开销就很明显,因为大量的TW套接字的insert和delete,须要频繁LOCK哈希表,这种开销彻底记账到了发送进程的名下,也是不公平的。
       注意,Linux内核中,softirq会在两种上下文中执行,一种是硬件中断后的任意上下文中,一种是每CPU一个内核线程的上下文中,后者会记账给top命令的si百分比,前者则会记账给任意被中断的进程。

redis

相关文章
相关标签/搜索