红联Linux门户
Linux帮助

Linux下高并发socket,单机提供五十万连接

发布时间:2017-09-07 11:01:45来源:blog.csdn.net/peng314899581作者:linux_c_coding_man
一个完整的Socket描述,五元组,{协议,本地地址,本地端口,远程地址,远程端口}
 
当我们自己写客户端测试程序来测试我们的服务端程序性能的时候,经常会遇到连接量上不去的问题,大多都是停留在2万左右。如果我们的测试机器有限,要想达到百万连接量的测试,那我们哪里去找50台测试机器了?实际的测试也不可能给50台测试机器。那么根据这个五元组,我们可以做一下文章。首先协议已经确定TCP,远程地址和远程端口也已经确定,我们的服务器程序绑定的地址和端口。那么剩下的就是本地地址和本地端口了。现在就可以这么说,本地地址和本地端口排列组合的数目就是连接的数量。
 
第一步,我们可以给网卡绑定多个IP,在客户端程序中分别bind这多个IP地址,然后connect出去,这时候的端口是系统自己选择的,端口是可以复用的,也就是连接数量等于(多个IP地址*多个端口号),可是结果不尽人意,你会发现连接量还是上不去,这是因为系统默认的端口号范围影响造成的。原因是因为Linux网络内核对本地端口号范围有限制。此时,进一步分析为什么无法建立TCP连接,会发现问题出在connect()调用返回失败,查看系统错误提示消息是“Can’t assign requestedaddress”。同时,如果在此时用tcpdump工具监视网络,会发现根本没有TCP连接时客户端发SYN包的网络流量。这些情况说明问题在于本地Linux系统内核中有限制。其实,问题的根本原因在于Linux内核的TCP/IP协议实现模块对系统中所有的客户端TCP连接对应的本地端口号的范围进行了限制(例如,内核限制本地端口号的范围为1024~32768之间)。当系统中某一时刻同时存在太多的TCP客户端连接时,由于每个TCP客户端连接都要占用一个唯一的本地端口号(此端口号在系统的本地端口号范围限制中),如果现有的TCP客户端连接已将所有的本地端口号占满,则此时就无法为新的TCP客户端连接分配一个本地端口号了,因此系统会在这种情况下在connect()调用中返回失败,并将错误提示消息设为“Can’t assignrequested address”。内核编译时默认设置的本地端口号范围可能太小,因此需要修改此本地端口范围限制。
修改/etc/sysctl.conf文件,在文件中添加如下行:
net.ipv4.ip_local_port_range = 1024 65000
这表明将系统对本地端口范围限制设置为1024~65000之间。请注意,本地端口范围的最小值必须大于或等于1024;而端口范围的最大值则应小于或等于65535。修改完后保存此文件。
执行sysctl命令:
$ sysctl -p
如果系统没有错误提示,就表明新的本地端口范围设置成功。如果按上述端口范围进行设置,则理论上单独一个线程最多可以同时建立60000多个TCP客户端连接。这个时候如果单个线程无法建立TCP连接的原因可能是因为Linux网络内核的IP_TABLE防火墙对最大跟踪的TCP连接数有限制。此时程序会表现为在 connect()调用中阻塞,如同死机,如果用tcpdump工具监视网络,也会发现根本没有TCP连接时客户端发SYN包的网络流量。由于 IP_TABLE防火墙在内核中会对每个TCP连接的状态进行跟踪,跟踪信息将会放在位于内核内存中的conntrackdatabase中,这个conntrackdatabase的大小有限,当系统中存在过多的TCP连接时,容量不足,IP_TABLE无法为新的TCP连接建立跟踪信息,于是表现为在connect()调用中阻塞。最简单的办法就是关闭防火墙IP_TABLE或者就修改内核对最大跟踪的TCP连接数的限制,方法同修改内核对本地端口号范围的限制是类似的:
修改/etc/sysctl.conf文件,在文件中添加如下行:
net.ipv4.ip_conntrack_max = 10240
这表明将系统对最大跟踪的TCP连接数限制设置为10240。请注意,此限制值要尽量小,以节省对内核内存的占用。
执行sysctl命令:
$ sysctl -p
如果系统没有错误提示,就表明系统对新的最大跟踪的TCP连接数限制修改成功。如果按上述参数进行设置,则理论上单独一个进程最多可以同时建立10000多个TCP客户端连接。
 
第二步,这个时候你可能还是会遇到错误“Too many open files”,再次引用《Linux网络编程》
使用UNIX的高手有这么一句话:“恩,在UNIX系统中,任何东西都是一个文件。”这句话描述了这样一个事实:在UNIX系统中,任何对I/O的操作,都是通过读或写一个文件描述符来实现的。一个文件描述符只是一个简单的整形数值,代表一个被打开的文件(这里的文件是广义的文件,并不只代表不同的磁盘文件,它可以代表一个网络上的连接,一个先进先出队列,一个终端显示屏幕,以及其他的一切)。在UNIX系统中任何东西都是一个文件!!所以如果你想通过Internet和另外一个程序通讯的话,你将会是通过一个文件来描述符实现的。好的,你已经相信Socket是一个文件描述符了。
这里你就明白了错误的原因了,这个时候是文件描述符上不去了,你确实能创建足够的连接了,但是代表连接的socket却不够了。
在Linux平台上,无论编写客户端程序还是服务端程序,在进行高并发TCP连接处理时,最高的并发数量都要受到系统对用户单一进程同时可打开文件数量的限制(这是因为系统为每个TCP连接都要创建一个socket句柄,每个socket句柄同时也是一个文件句柄)。可使用ulimit命令查看系统允许当前用户进程打开的文件数限制:
$ ulimit -n
1024
这表示当前用户的每个进程最多允许同时打开1024个文件,这1024个文件中还得除去每个进程必然打开的标准输入,标准输出,标准错误,服务器监听 socket,进程间通讯的unix域socket等文件,那么剩下的可用于客户端socket连接的文件数就只有大概1024-10=1014个左右。也就是说缺省情况下,基于Linux的通讯程序最多允许同时1014个TCP并发连接。
对于想支持更高数量的TCP并发连接的通讯处理程序,就必须修改Linux对当前用户的进程同时打开的文件数量的软限制(soft limit)和硬限制(hardlimit)。其中软限制是指Linux在当前系统能够承受的范围内进一步限制用户同时打开的文件数;硬限制则是根据系统硬件资源状况(主要是系统内存)计算出来的系统最多可同时打开的文件数量。通常软限制小于或等于硬限制。
修改上述限制的最简单的办法就是使用ulimit命令:
$ ulimit -n
上述命令中,在中指定要设置的单一进程允许打开的最大文件数。如果系统回显类似于“Operation notpermitted”之类的话,说明上述限制修改失败,实际上是因为在中指定的数值超过了Linux系统对该用户打开文件数的软限制或硬限制。因此,就需要修改Linux系统对用户的关于打开文件数的软限制和硬限制。
修改/etc/security/limits.conf文件,在文件中添加如下行:
user soft nofile 10240
user hard nofile 10240
其中user指定了要修改哪个用户的打开文件数限制,可用'*'号表示修改所有用户的限制;soft或hard指定要修改软限制还是硬限制;10240则指定了想要修改的新的限制值,即最大打开文件数(请注意软限制值要小于或等于硬限制)。这个数值可以参考最后一步Linux系统级硬限制的数量,比这个数量小就行了,修改完后保存文件。
修改/etc/pam.d/login文件,在文件中添加如下行:
session required /lib/security/pam_limits.so
这是告诉Linux在用户完成系统登录后,应该调用pam_limits.so模块来设置系统对该用户可使用的各种资源数量的最大限制(包括用户可打开的最大文件数限制),而pam_limits.so模块就会从/etc/security/limits.conf文件中读取配置来设置这些限制值。修改完后保存此文件。
查看Linux系统级的最大打开文件数限制,使用如下命令:
$ cat /proc/sys/fs/file-max
12158
这表明这台Linux系统最多允许同时打开(即包含所有用户打开文件数总和)12158个文件,是Linux系统级硬限制,所有用户级的打开文件数限制都不应超过这个数值。然后保存文件,用户退出并重新登录系统即可。这样就为支持高并发TCP连接处理的通讯处理程序解除关于打开文件数量方面的系统限制。
 
最后单机客户端测试程序提供五十万连接就没有问题了。
 
本文永久更新地址:http://www.linuxdiyf.com/linux/28982.html