首页 » SEO优化 » php衔接ip耗时技巧_聊聊TCP连接耗时的那些事儿

php衔接ip耗时技巧_聊聊TCP连接耗时的那些事儿

访客 2024-12-15 0

扫一扫用手机浏览

文章目录 [+]

在这些调用的底层,基本都是在利用TCP协议进行传输。
这是由于在传输层协议中,TCP协议具备可靠的连接,缺点重传,拥塞掌握等优点,以是目前运用比UDP更广泛一些。

相信你也一定听闻过TCP也存在一些缺陷,那便是旧调重弹的开销要略大。
但是各路技能博客里都在单单说开销大、或者开销小,而少见不给出详细的量化剖析。
不客气一点,这都是营养不大的废话。

php衔接ip耗时技巧_聊聊TCP连接耗时的那些事儿

经由日常事情的思考之后,我更想弄明白的是,开销到底多大。
一条TCP连接的建立须要耗时延迟多少,是多少毫秒,还是多少微秒?能不能有一个哪怕是粗略的量化估计?当然影响TCP耗时的成分有很多,比如网络丢包等等。
我本日只分享我在事情实践中碰着的比较高发的各种情形。

php衔接ip耗时技巧_聊聊TCP连接耗时的那些事儿
(图片来自网络侵删)

正常TCP连接建立过程

要想搞清楚TCP连接的建立耗时,我们须要详细理解连接的建立过程。
在前文《图解Linux网络包吸收过程》中我们先容了数据包在吸收端是怎么被吸收的。
数据包从发送方出来,经由网络到达吸收方的网卡。
在吸收方网卡将数据包DMA到RingBuffer后,内核经由硬中断、软中断等机制来处理(如果发送的是用户数据的话,末了会发送到socket的吸收行列步队中,并唤醒用户进程)。

在软中断中,当一个包被内核从RingBuffer中摘下来的时候,在内核中是用struct sk_buff构造体来表示的(拜会内核代码include/linux/skbuff.h)。
个中的data成员是吸收到的数据,在协议栈逐层被处理的时候,通过修正指针指向data的不同位置,来找到每一层协议关心的数据。

对付TCP协议包来说,它的Header中有一个主要的字段-flags。
如下图:

通过设置不同的标记为,将TCP包分成SYNC、FIN、ACK、RST等类型。
客户端通过connect系统调用命令内核发出SYNC、ACK等包来实现和做事器TCP连接的建立。
在做事器端,可能会吸收许许多多的连接要求,内核还须要借助一些赞助数据构造-半连接行列步队和全连接行列步队。
我们来看一下全体连接过程:

在这个连接过程中,我们来大略剖析一下每一步的耗时

客户端发出SYNC包:客户端一样平常是通过connect系统调用来发出SYN的,这里牵扯到本机的系统调用和软中断的CPU耗时开销SYN传到做事器:SYN从客户端网卡被发出,开始“跨过山和大海,也穿过人隐士海......”,这是一次长途远间隔的网络传输做事器处理SYN包:内核通过软中断来收包,然后放到半连接行列步队中,然后再发出SYN/ACK相应。
又是CPU耗时开销SYC/ACK传到客户端:SYC/ACK从做事器端被发出后,同样跨过很多山、可能很多大海来到客户端。
又一次长途网络跋涉客户端处理SYN/ACK:客户端内核收包并处理SYN后,经由几us的CPU处理,接着发出ACK。
同样是软中断处理开销ACK传到做事器:和SYN包,一样,再经由险些同样远的路,传输一遍。
又一次长途网络跋涉做事端收到ACK:做事器端内核收到并处理ACK,然后把对应的连接从半连接行列步队中取出来,然后放到全连接行列步队中。
一次软中断CPU开销做事器端用户进程唤醒:正在被accpet系统调用壅塞的用户进程被唤醒,然后从全连接行列步队中取出来已经建立好的连接。
一次高下文切换的CPU开销

以上几步操作,可以大略划分为两类:

第一类是内核花费CPU进行吸收、发送或者是处理,包括系统调用、软中断和高下文切换。
它们的耗时基本都是几个us旁边。
详细的剖析过程可以拜会《一次系统调用开销到底有多大?》、《软中断会吃掉你多少CPU?》、《进程/线程切换会用掉你多少CPU?》这三篇文章。
第二类是网络传输,当包被从一台机器上发出往后,中间要经由各式各样的网线、各种交流机路由器。
以是网络传输的耗时比较本机的CPU处理,就要高的多了。
根据网络远近一样平常在几ms~到几百ms不等。

1ms就即是1000us,因此网络传输耗时比双真个CPU开销要高1000倍旁边,乃至更高可能还到100000倍。
以是,在正常的TCP连接的建立过程中,一样平常可以考虑网络延时即可。
一个RTT指的是包从一台做事器到其余一台做事器的一个来回的延迟韶光。
以是从全局来看,TCP连接建立的网络耗时大约须要三次传输,再加上少许的双方CPU开销,统共大约比1.5倍RTT大一点点。
不过从客户端视角来看,只要ACK包发出了,内核就认为连接是建立成功了。
以是如果在客户端打点统计TCP连接建立耗时的话,只须要两次传输耗时-既1个RTT多一点的韶光。
(对付做事器端视角来看同理,从SYN包收到开始算,到收到ACK,中间也是一次RTT耗时)

TCP连接建立时的非常情形

上一节可以看到在客户端视角,在正常情形下一次TCP连接总的耗时也就就大约是一次网络RTT的耗时。
如果所有的事情都这么大略,我想我的这次分享也就没有必要了。
事情不一定总是这么美好,总会有意外发生。
在某些情形下,可能会导致连接时的网络传输耗时上涨、CPU处理开销增加、乃至是连接失落败。
现在我们说一下我在线上碰着过的各种沟沟坎坎。

1)客户端connect系统调用耗时失落控

正常一个别系调用的耗时也便是几个us(微秒)旁边。
但是在《追踪将做事器CPU耗光的凶手!》一文中笔者的一台做事器当时碰着一个状况,某次运维同学转达过来说该做事CPU不足用了,须要扩容。
当时的做事器监控如下图:

该做事之前一贯每秒抗2000旁边的qps,CPU的idel一贯有70%+。
怎么溘然就CPU一下就不足用了呢。
而且更奇怪的是CPU被打到谷底的那一段韶光,负载却并不高(做事器为4核机器,负载3-4是比较正常的)。
后来经由排查往后创造当TCP客户端TIME_WAIT有30000旁边,导致可用端口不是特殊充足的时候,connect系统调用的CPU开销直接上涨了100多倍,每次耗时达到了2500us(微秒),达到了毫秒级别。

当碰着这种问题的时候,虽然TCP连接建立耗时只增加了2ms旁边,整体TCP连接耗时看起来还可接管。
但是这里的问题在于这2ms多都是在花费CPU的周期,以是问题不小。
办理起来也非常大略,办法很多:修正内核参数net.ipv4.ip_local_port_range多预留一些端口号、改用长连接都可以。

2)半/全连接行列步队满

如果连接建立的过程中,任意一个行列步队满了,那么客户端发送过来的syn或者ack就会被丢弃。
客户端等待很长一段韶光无果后,然后会发出TCP Retransmission重传。
拿半连接行列步队举例:

要知道的是上面TCP握手超时重传的韶光是秒级别的。
也便是说一旦server真个连接行列步队导致连接建立不堪利,那么光建立连接就至少须要秒级以上。
而正常的在同机房的情形下只是不到1毫秒的事情,整整高了1000倍旁边。
尤其是对付给用户供应实时做事的程序来说,用户体验将会受到较大影响。
如果连重传也没有握手成功的话,很可能等不及二次重试,这个用户访问直接就超时了。

还有其余一个更坏的情形是,它还有可能会影响其它的用户。
如果你利用的是进程/线程池这种模型供应做事,比如php-fpm。
我们知道fpm进程是壅塞的,当它相应一个用户要求的时候,该进程是没有办法再相应其它要求的。
如果你开了100个进程/线程,而某一段韶光内有50个进程/线程卡在和redis或者mysql做事器的握手连接上了(把稳:这个时候你的做事器是TCP连接的客户端一方)。
这一段韶光内相称于你可以用的正常事情的进程/线程只有50个了。
而这个50个worker可能根本处理不过来,这时候你的做事可能就会产生拥堵。
再持续轻微韶光长一点的话,可能就产生雪崩了,全体做事都有可能会受影响。

既然后果有可能这么严重,那么我们如何查看我们手头的做事是否有由于半/全连接行列步队满的情形发生呢?在客户端,可以抓包查看是否有SYN的TCP Retransmission。
如果有偶发的TCP Retransmission,那就解释对应的做事端连接行列步队可能有问题了。

在做事真个话,查看起来就更方便一些了。
netstat -s可查看到当前系统半连接行列步队满导致的丢包统计,但该数字记录的是总丢包数。
你须要再借助watch命令动态监控。
如果下面的数字在你监控的过程中变了,那解释当前做事器有由于半连接行列步队满而产生的丢包。
你可能须要加大你的半连接行列步队的长度了。

$ watch 'netstat -s | grep LISTEN' 8 SYNs to LISTEN sockets ignored

对付全连接行列步队来说呢,查看方法也类似。

$ watch 'netstat -s | grep overflowed' 160 times the listen queue of a socket overflowed

如果你的做事由于行列步队满产生丢包,个中一个做法便是加大半/全连接行列步队的长度。
半连接行列步队长度Linux内核中,紧张受tcp_max_syn_backlog影响 加大它到一个得当的值就可以。

# cat /proc/sys/net/ipv4/tcp_max_syn_backlog1024# echo "2048" > /proc/sys/net/ipv4/tcp_max_syn_backlog

全连接行列步队长度是运用程序调用listen时传入的backlog以及内核参数net.core.somaxconn二者之中较小的那个。
你可能须要同时调度你的运用程序和该内核参数。

# cat /proc/sys/net/core/somaxconn128# echo "256" > /proc/sys/net/core/somaxconn

改完之后我们可以通过ss命令输出的Send-Q确认最终生效长度:

$ ss -nltRecv-Q Send-Q Local Address:Port Address:Port0 128 :80 :

Recv-Q见告了我们当前该进程的全连接行列步队利用长度情形。
如果Recv-Q已经逼近了Send-Q,那么可能不须要等到丢包也该当准备加大你的全连接行列步队了。

如果加大行列步队后仍旧有非常偶发的行列步队溢出的话,我们可以暂且容忍。
如果仍旧有较永劫光处理不过来怎么办?其余一个做法便是直接报错,不要让客户端超时等待。
例如将Redis、Mysql等后端接口的内核参数tcp_abort_on_overflow为1。
如果行列步队满了,直接发reset给client。
见告后端进程/线程不要痴情地傻等。
这时候client会收到缺点“connection reset by peer”。
捐躯一个用户的访问要求,要比把全体站都搞崩了还是要强的。

TCP连接耗时实测

我写了一段非常大略的代码,用来在客户端统计每创建一个TCP连接须要花费多永劫光。

<?php$ip = {做事器ip};$port = {做事器端口};$count = 50000;function buildConnect($ip,$port,$num){ for($i=0;$i<$num;$i++){ $socket = socket_create(AF_INET,SOCK_STREAM,SOL_TCP); if($socket ==false) { echo "$ip $port socket_create() 失落败的缘故原由是:".socket_strerror(socket_last_error($socket))."\n"; sleep(5); continue; } if(false == socket_connect($socket, $ip, $port)){ echo "$ip $port socket_connect() 失落败的缘故原由是:".socket_strerror(socket_last_error($socket))."\n"; sleep(5); continue; } socket_close($socket); }}$t1 = microtime(true);buildConnect($ip, $port, $count);echo (($t2-$t1)1000).'ms';

在测试之前,我们须要本机linux可用的端口数充足,如果不足50000个,最好调度充足。

# echo "5000 65000" /proc/sys/net/ipv4/ip_local_port_range

1)正常情形把稳:无论是客户端还是做事器端都不要选择有线上做事在跑的机器,否则你的测试可能会影响正常用户访问

首先我的客户端位于河北怀来的IDC机房内,做事器选择的是公司广东机房的某台机器。
实行ping命令得到的延迟大约是37ms,利用上述脚本建立50000次连接后,得到的连接均匀耗时也是37ms。
这是由于前面我们说过的,对付客户端来看,第三次的握手只要包发送出去,就认为是握手成功了,以是只须要一次RTT、两次传输耗时。
虽然这中间还会有客户端和做事真个系统调用开销、软中断开销,但由于它们的开销正常情形下只有几个us(微秒),以是对总的连接建立延时影响不大。

接下来我换了一台目标做事器,该做事器所在机房位于北京。
离怀来有一些间隔,但是和广东比起来可要近多了。
这一次ping出来的RTT是1.6~1.7ms旁边,在客户端统计建立50000次连接后算出每条连接耗时是1.64ms。

再做一次实验,这次选中实验的做事器和客户端直接位于同一个机房内,ping延迟在0.2ms~0.3ms旁边。
跑了以上脚本往后,实验结果是50000 TCP连接统共花费了11605ms,均匀每次须要0.23ms。

线上架构提示:这里看到同机房延迟只有零点几ms,但是跨个间隔不远的机房,光TCP握手耗时就涨了4倍。
如果再假如跨地区到广东,那便是百倍的耗时差距了。
线上支配时,空想的方案是将自己做事依赖的各种mysql、redis等做事和自己支配在同一个地区、同一个机房(再变态一点,乃至可以是乃至是同一个机架)。
由于这样包括TCP链接建立啥的各种网络包传输都要快很多。
要尽可能避免长途跨地区机房的调用情形涌现。

2)连接行列步队溢出

测试完了跨地区、跨机房和跨机器。
这次为了快,直接和本机建立连接结果会咋样呢?Ping本机ip或127.0.0.1的延迟大概是0.02ms,本机ip比其它机器RTT肯定要短。
我以为肯定连接会非常快,嗯实验一下。
连续建立5W TCP连接,总韶光花费27154ms,均匀每次须要0.54ms旁边。
嗯!
?怎么比跨机器还长很多? 有了前面的理论根本,我们该当想到了,由于本机RTT太短,以是瞬间连接建立要求量很大,就会导致全连接行列步队或者半连接行列步队被打满的情形。
一旦发生行列步队满,当时撞上的那个连接要求就得须要3秒+的连接建立延时。
以是上面的实验结果中,均匀耗时看起来比RTT高很多。

在实验的过程中,我利用tcpdump抓包看到了下面的一幕。
原来有少部分握手耗时3s+,缘故原由是半连接行列步队满了导致客户端等待超时后进行了SYN的重传。

我们又重新改成每500个连接,sleep 1秒。
嗯好,终于没有卡的了(或者也可以加大连接行列步队长度)。
结论是本机50000次TCP连接在客户端统计总耗时102399 ms,减去sleep的100秒后,均匀每个TCP连接花费0.048ms。
比ping延迟略高一些。
这是由于当RTT变的足够小的时候,内核CPU耗时开销就会显现出来了,其余TCP连接要比ping的icmp协议更繁芜一些,以是比ping延迟略高0.02ms旁边比较正常。

结论

TCP连接建立非常情形下,可能须要好几秒,一个坏处便是会影响用户体验,乃至导致当前用户访问超时都有可能。
其余一个坏处是可能会诱发雪崩。
以是当你的做事器利用短连接的办法访问数据的时候,一定要学会要监控你的做事器的连接建立是否有非常状态发生。
如果有,学会优化掉它。
当然你也可以采取本机内存缓存,或者利用连接池来保持长连接,通过这两种办法直接避免掉TCP握手挥手的各种开销也可以。

再说正常情形下,TCP建立的延时大约便是两台机器之间的一个RTT耗时,这是避免不了的。
但是你可以掌握两台机器之间的物理间隔来降落这个RTT,比如把你要访问的redis尽可能地支配的离后端接口机器近一点,这样RTT也能从几十ms减少到最低可能零点几ms。

末了我们再思考一下,如果我们把做事器支配在北京,给纽约的用户访问可行吗? 前面的我们同机房也好,跨机房也好,电旗子暗记传输的耗时基本可以忽略(由于物理间隔很近),网络延迟基本上是转发设备占用的耗时。
但是如果是超过了半个地球的话,电旗子暗记的传输耗时我们可得算一算了。
北京到纽约的球面间隔大概是15000公里,那么抛开设备转发延迟,仅仅光速传播一个来回(RTT是Rround trip time,要跑两次),须要韶光 = 15,000,000 2 / 光速 = 100ms。
实际的延迟可能比这个还要大一些,一样平常都得200ms以上。
建立在这个延迟上,要想供应用户能访问的秒级做事就很困难了。
以是对付外洋用户,最好都要在当地建机房或者购买外洋的做事器。

作者:张彦飞allen

标签:

相关文章

大数据分析在现代社会的应用与影响

随着互联网技术的飞速发展,大数据已成为推动社会进步的重要力量。大数据分析作为一门交叉学科,融合了统计学、计算机科学、信息科学等多个...

SEO优化 2024-12-17 阅读0 评论0

大数据倒流,未来数据经济的颠覆性变革

随着互联网的飞速发展,大数据已经成为各行各业的重要资源。近年来,大数据倒流现象逐渐兴起,它不仅改变了数据的使用方式,更对未来数据经...

SEO优化 2024-12-17 阅读0 评论0

大数据刘海,介绍互联网时代的隐私防线

随着互联网的快速发展,大数据时代已经来临。在这个时代,个人信息被广泛收集、分析和利用,而大数据刘海作为一种新型的隐私保护技术,逐渐...

SEO优化 2024-12-17 阅读0 评论0