长连接,是指通信双方有数据交互时,就建立一个TCP连接,数据发送完成后,则断开此TCP连接。
聊天室或即时消息推送系统等,因为很多消息需要到产生时才推送给客户端,所以当没有消息产生时,就需要hold住客户端的连接,这样,当有大量的客户端时,要hold住大量的长连接。
在性能测试过程中,经常会接触到连接数相关的问题,有一个问题曾经困扰我好长时间,那就是一台服务器最多能支持多少链接数呢?
有的朋友可能会说是65535,因为 *** 作系统有65535个端口,那么这个答案准确吗?
首先先了解下如何标识一个链接(记住下面的概念,文章后面要用到), *** 作系统是通过一个四元组来标识一个TCP链接:
{本地ip,本地port,远程ip,远程port}
这四个要素唯一确定一个TCP链接,任意一个要素不相同,就认为是一个不同的链接。
在Linux系统中,一切皆文件,每一个TCP链接都要占用一个文件句柄,系统允许创建的链接数取决于句柄数的上限。超过这个值再创建链接就会报这样的错误:“Can't open so many files"。
通过命令ulimit -n可以查看当前系统允许打开文件数量的上限,在Linux中这个值默认是1024,也就是说默认情况下,只能创建1024个链接。同时这个值也是可以修改的,通过修改/etc/security/limitsconf文件,可以把这个值改大,一般服务器都会改的很大,比如我们的服务器上一般设置为1000000。
那这么说是不是就意味着只要我改的很大,链接数可以无限大了?
其实也并不是这样,创建链接的时候,一般分为两个端, 即链接的发起端和链接接收端。
比如我们现在使用Jmeter进行压测,被测系统部署在Tomcat服务器10003上,使用的是8080端口。
如果我们用5个并发来进行压测的话,创建的链接如下图所示:
对于Jmeter来说,它是链接发起端,Jmeter创建了5个链接去连接服务端的8080端口,每个新建链接会占用了一个端口号,如图中的10001-10005。在 *** 作系统中,端口号的范围是0-65535,其中0-1024是预留端口号,不可使用,其他的端口都是可以使用的。也就是说, 在链接发起端,受端口号的限制理论上最多可以创建64000左右链接。
那么有没有办法超过这个限制呢,答案是肯定的!
通过TCP标识的四元组可以看到,对于链接发起端,影响链接数的是本地ip和port,端口号受限于65535,已经没办法增加了。那我们可以增加本地ip来达到这个目的。一般情况下,服务器的一个网卡上只绑定了一个ip,对外通信都使用这个ip进行。其实网卡是支持一个绑定多个IP的,当然必须确保ip是有效的且未使用的。
# ifconfig eth0:1 10005
以上命令可以在eth0网卡上增加一个ip 10005,服务器网卡每增加一个ip,就可以允许在这个ip上再创建65535左右的链接数。
曾经做过一个邮件网关的链接数测试,目的是为了测试网关服务器可以接收并且保持多少TCP长连接。正常情况下,受限于单台机器65535端口号的影响,客户端想创建25万TCP长连接,至少需要4台机器。通过对客户端网卡绑定多IP的方法,成功在一台机器上创建了25万个链接。
当然,这种手段只是一种非常规的 *** 作,只是为了进行某种特殊场景的测试。正常情况下不推荐网卡绑定多个IP。
对于Tomcat服务器来讲,它是链接接收端,它是不是也受限于65535呢?
并不是,从上面图中可以看到,Jmeter发起的所有链接都创建在Tomcat服务器的8080端口,也就是说对于链接接收端,所有的链接占用的是同一个端口。
根据TCP标识四元组可以分析出, 一个链接接收端,最大的TCP链接数=所有有效ip排列组合的数量端口数量64000 ,这个计算结果应该是一个天文数字。 因此链接接收端支持的链接数理论上可以认为是无限大的。
上面介绍的一些数据都是理论上单台机器可以支持的TCP链接数, 实际情况下,每创建一个链接需要消耗一定的内存,大概是4-10kb,所以链接数也受限于机器的总内存。
链接发起端,活力全开才64000左右链接,内存最多才占用640M,一般客户端都能 满足,内存限制主要还是考虑服务器端。
虽然现在的集群,分布式技术可以为我们将并发负载分担在多台服务器上,那我们只需要扩展出数十台电脑就可以解决问题,但是我们更希望能更大的挖掘单台服务器的资源,先努力垂直扩展,再进行水平扩展,这样可以有效的节省服务器相关的开支(硬件资源、机房、运维人力、电力其实也是一笔不小的开支)。
首先需要考虑文件句柄的限制。在Linux下编写网络服务器程序的朋友肯定都知道每一个tcp连接都要占一个文件描述符,一旦这个文件描述符使用完了,新的连接到来返回给我们的错误是“Socket/File:Can't open so many files”。这时你需要明白 *** 作系统对可以打开的最大文件数的限制。
我们可以通过ulimit -n命令、/etc/security/limitsconf 文件 以及 /etc/sysctlconf 文件等来修改文件句柄数。
其次要考虑的是端口范围的限制, *** 作系统上端口号1024以下是系统保留的,从1024-65535是用户使用的。
由于每个TCP连接都要占一个端口号,所以我们最多可以有60000多个并发连接。我想有这种错误思路朋友不在少数吧?
面试官也比较喜欢在这里引导挖坑,类似的问题还有:一个UDP连接可以复用已经被TCP连接占用的端口嘛?
如何标识一个TCP连接?
系统使用一个4四元组来唯一标识一个TCP连接:
本地端口号 local port、本地IP地址 local ip、远端端口号 remote port、远端IP地址 remote ip。
server通常固定在某个本地端口上监听,等待client的连接请求。不考虑地址重用(unix的SO_REUSEADDR选项)的情况下,即使server端有多个ip,本地监听端口也是独占的,因此server端tcp连接4元组中只有remote ip(也就是client ip)和remote port(客户端port)是可变的,因此最大tcp连接为客户端ip数×客户端port数,对IPV4,不考虑ip地址分类等因素,最大tcp连接数约为2的32次方(ip数)×2的16次方(port数),也就是server端单机最大tcp连接数约为2的48次方。
上面给出的结论都是理论上的单机TCP并发连接数,实际上单机并发连接数肯定要受硬件资源(内存)、网络资源(带宽)的限制。
单台服务器最大支持多少连接数
>
在日常的服务器维护中,会经常用到如下命令。
netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'
它会显示例如下面的信息:
TIME_WAIT 689
CLOSE_WAIT 2
FIN_WAIT1 1
ESTABLISHED 291
SYN_RECV 2
LAST_ACK 1
常用的三个状态是:ESTABLISHED表示正在通信 、TIME_WAIT表示主动关闭、CLOSE_WAIT表示被动关闭。
如果服务器出现了异常,很大的可能是出现了以下两种情况:
我们也都知道Linux系统中分给每个用户的文件句柄数是有限的,而TIME_WAIT和CLOSE_WAIT这两种状态如果一直被保持,那么意味着对应数目的通道(此处应理解为socket,一般一个socket会占用服务器端一个端口,服务器端的端口最大数是65535)一直被占用,一旦达到了上限,则新的请求就无法被处理,接着就是大量Too Many Open Files异常,然后tomcat、nginx、apache崩溃。。。
下面来讨论这两种状态的处理方法,网络上也有很多资料把这两种情况混为一谈,认为优化内核参数就可以解决,其实这是不恰当的。优化内核参数在一定程度上能解决time_wait过多的问题,但是应对close_wait还得从应用程序本身出发。
这种情况比较常见,一般会出现在爬虫服务器和web服务器(如果没做内核参数优化的话)上,那么这种问题是怎么产生的呢?
从上图可以看出time_wait是主动关闭连接的一方保持的状态,对于爬虫服务器来说它自身就是客户端,在完成一个爬取任务后就会发起主动关闭连接,从而进入time_wait状态,然后保持这个状态2MSL时间之后,彻底关闭回收资源。这里为什么会保持资源2MSL时间呢?这也是TCP/IP设计者规定的。
TCP要保证在所有可能的情况下使得所有的数据都能够被正确送达。当你关闭一个socket时,主动关闭一端的socket将进入TIME_WAIT状 态,而被动关闭一方则转入CLOSED状态,这的确能够保证所有的数据都被传输。当一个socket关闭的时候,是通过两端四次握手完成的,当一端调用 close()时,就说明本端没有数据要发送了。这好似看来在握手完成以后,socket就都可以处于初始的CLOSED状态了,其实不然。原因是这样安 排状态有两个问题, 首先,我们没有任何机制保证最后的一个ACK能够正常传输,第二,网络上仍然有可能有残余的数据包(wandering duplicates),我们也必须能够正常处理。
TIMEWAIT就是为了解决这两个问题而生的。
再引用网络中的一段话:
time_wait问题可以通过调整内核参数和适当的设置web服务器的keep-Alive值来解决。因为time_wait是自己可控的,要么就是对方连接的异常,要么就是自己没有快速的回收资源,总之不是由于自己程序错误引起的。但是close_wait就不一样了,从上图中我们可以看到服务器保持大量的close_wait只有一种情况,那就是对方发送一个FIN后,程序自己这边没有进一步发送ACK以确认。换句话说就是在对方关闭连接后,程序里没有检测到,或者程序里本身就已经忘了这个时候需要关闭连接,于是这个资源就一直被程序占用着。这个时候快速的解决方法是:
注:
直到写这篇文章的时候我才完全弄明白之前工作中遇到的一个问题。程序员写了爬虫(php)运行在采集服务器A上,程序去B服务器上采集资源,但是A服务器很快就发现出现了大量的close_wait状态的连接。后来手动检查才发现这些处于close_wait状态的请求结果都是404,那就说明B服务器上没有要请求的资源。
下面引用网友分析的结论:
服 务器A是一台爬虫服务器,它使用简单的>linuxusermaxprocess的作用
推荐调大服务器所支持的最大文件句柄数(File Descriptor,简写为fd)。
说明:主流 *** 作系统的设计是将 TCP/UDP 连接采用与文件一样的方式去管理,即一个连接对应于一个 fd。主流的 Linux 服务器默认所支持最大 fd 数量为 1024,当并发连接数很大时很容易因为 fd 不足而出现“open too many files”错误,导致新的连接无法建立。 建议将 Linux 服务器所支持的最大句柄数调高数倍(与服务器的内存数量相关)
来看下负载的定义是怎样的:
In UNIX computing, the system load is a measure of the amount of computational work that a computer system performs The load average represents the average system load over a period of time It conventionally appears in the form of three numbers which represent the system load during the last one-, five-, and fifteen-minute periods(wikipedia)
Unix refers to this as the run-queue length: the sum of the number of processes that are currently running plus the number that are waiting (queued) to run
Free memory is the amount of memory which is currently not used for anything This number should be small, because memory which is not used is simply wasted
Available memory is the amount of memory which is available for allocation to a new process or to existing processes。
df
查看磁盘使用情况,通常看磁盘大小和inode使用率:
磁盘性能分析
r/s 和 w/s:每秒磁盘读写的次数。这两个值相加就是 tps。
rkB/s 和 wkB/s:每秒磁盘读写的数据量。
avgrq-sz:平均每次读写磁盘扇区的大小。
avgqu-sze:平均 IO 队列长度。队列长度越短越好。
await:平均每次磁盘读写的等待时间(ms)。
svctm:平均每次磁盘读写的服务时间(ms)。
%util:一秒钟有百分之多少的时间用于磁盘读写 *** 作。
1)%util:衡量 IO 的繁忙程度
这个值越大,说明产生的 IO 请求较多,IO 压力较大,
我们可以结合 %idle 参数来看,如果 %idle < 70% 就说明 IO 比较繁忙了。
2)await:衡量 IO 的响应速度
通俗理解,await 就像我们去医院看病排队等待的时间,
这个值和医生的服务速度(svctm)和你前面排队的人数(avgqu-size)有关。
如果 svctm 和 await 接近,说明磁盘 IO 响应时间较快,排队较少,
如果 await 远大于 svctm,说明此时队列太长,响应较慢,
这时可以考虑换性能更好的磁盘。
带宽:表示链路的最大传输速率,单位通常为 b/s (比特 / 秒)
延时:表示从网络请求发出后,一直到收到远端响应,所需要的时间延迟
在不同场景中,这一指标可能会有不同含义
比如,它可以表示,建立连接需要的时间(比如 TCP握手延时)
或一个数据包往返所需的时间(比如 RTT)
PPS:是 Packet Per Second(包 / 秒)的缩写,表示以网络包为单位的传输速率�丢包率:丢包百分比
重传率:重新传输的网络包比例
连接数状态:TCP 各状态连接数量
TIME_WAIT状态存在有两个原因。
第一个是防止来自一个连接的延迟段被误解为后续连接的一部分。
连接处于2MSL等待状态时到达的所有流量都将被丢弃。
该TIME_WAIT状态的第二个原因是
可靠地实现TCP的全双工连接终止。
如果最后的ACK被丢弃,那么端点2将重新发送最后的FIN
单机最大连接数理论限制
系统用一个4四元组来唯一标识一个TCP连接: �{local ip, local port, remote ip, remote port}。 �
因此本地端口个数最大只有65536,端口0有特殊含义,不能使用,
这样可用端口最多只有65535,
所以在全部作为client端的情况下,
最大tcp连接数为65535,这些连接可以连到不同的server ip
1、系统最大打开文件数
sysfsfilesmax //系统最大文件句柄数
/proc/sys/fs/file-max
2、单进程最大文件描述符
echo 2000000 > /proc/sys/fs/nr_open
sysctl -w fsnr_open=100000000
3、某个用户下的某个进程的文件打开数
ulimit –n [num]
ulimit -n unlimited
/etc/security/limitsconf
worker soft nofile 102400
worker hard nofile 409600
linux内核通过进程标识值(process identification value)-PID来标示进程,
PID是一个数,类型位pid_t, 实际上就是int类型
查看
可以使用cat /proc/sys/kernel/pid_max来查看系统中可创建的进程数实际值
修改
1、ulimit -u 65535
2、我们在Linux还需要设置内核参数kernelpid_maxsysctl -w kernelpid_max=65535
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)