最近在搞linux高并发,说白了就是搞个支持单进程高并发(百万级)的服务端,linux平台的,使用tcp协议。
单进程高并发,把这个些任务分成三步走:
1、首先系统能够建立起很多的链接。
2、这么多链接的消息都能及时回复
3、服务端拓展功能(超时、心跳、回调、业务接口等)
这次就说说第一步走的坑
1、服务端:
linux的socket资源服从于文件系统的限制和网络资源的限制,分别从小到大说下有坑的地方:
a、代码级:自己写代码,分配存放客户端的数据结构,总要有些最大的限制,这个限制都在自己手里控制。结构体定义小一点,我的客户端结构体大小就ip、端口、句柄、消息缓存的指针和长度,结构体大概50字节的长度,如果能跑到100W路,大概占1,000,000*50=50,000,000的内存,不过我还是动态分配的,内存不连续,不过我怀疑真到100W了,可能会core。
b、进程、用户级:ulimit里面的打开文件数nofile和栈大小适量的放开一下,但注意打开文件数不要太大,要不然操作系统进不去了,我用的是CentOS7,nofile超过1,024,000,重启操作系统直接就起不来了,需要进救援模式改回来才能正常启动。栈大小目前我是10M多一点,反正不够就core了,core了再说。
c、系统级:sysctl里面的系统参数有几个影响句柄最大数量和网络资源的上限,如下:
fs.file-max :文件系统最大文件打开数,目前我配了2097152
net.netfilter.nf_conntrack_max:网络最大连接数,目前我配了2097152
net.ipv4.ip_local_port_range :系统给用户分配的端口范围,目前我配了1023 65535
可以看到,程序级在堆栈不溢出的情况下,限制大概在100W-以上,进程、用户级在100W多一点点,系统级在200W。看其他人有这些参数非常科学的配置参考,但是毕竟是为了体现自己产品的能力,必须要放开,至于为啥是这些值,只能说多试吧。
2、客户端:客户端是个坑,功能简单,按照我们第一步的目标,想办法快速的connect就行了,不过从一台机器上给同一个ip分配的端口也就几万(linux放开了大概2W5,windows放开了大概5W),上哪里去找40台服务器或者20台windows办公电脑,去让其他组的同事配合做测试,也挺不好意思的,给人家链接占完了,打开个网页都卡。
3、其他:
a、服务端的代码就不贴了,不过既然是单进程的多路复用,linux看cpu占用率,在大量的客户端一起来链接的时候,一个逻辑核直接顶到100%,最后每秒大概能建立起700个链接,在想有没有办法利用到多核的能力。
b、我有两个windows主机,每个windows主机建立5W个连接去连服务端,当这10W个连接建立好之后,直接同时关闭两个windows主机上的客户端,那么这10W个连接会在服务端哪里疯狂断线,这种断线还不是closesocket()的那种正常挥手断开,这导致服务端最后经常会剩下几百或者几千个连接还是ESTABLISH状态,但是再也收不到这些socket上的信号了(半连接状态),这种情况下的服务端资源如何回收?
c、接着上面的场景,服务端原来建立了10W个连接,两个客户端离线之后,还剩下一些socket没有清理完成,系统中用netstat查看仍然是ESTABLISH状态,这时候再启动一个客户端,就会发现如下情况:
如图,上面的某个fd:75111,是服务端172.21.29.132:33233和172.21.29.71:47044的socket,再野蛮的关掉客户端之后,socket处在半连接的状态,再次启动客户端,在同一个连接(172.21.29.71:47044)建立到来时,epoll_wait会先收到一个RST的信号,然后立即给出连接建立的信号,这倒也好理解,内核发现原来记录的连接又来了三次握手,自然而然就知道之前对方断掉了,那么先通知应用进程回收之前的资源,可问题就是,真的好慢啊,可以从前面的时间戳看到,原本正常建立连接,大概1s能建立几百条,可是后面的速度就变成了1s左右,不知道怎么回事。我就不瞎猜了。
d、性能统计方面,服务端在性能统计方面相当的被动,指标非常的少,服务端看到的是一个一个独立的连接,虽然可以计算出连接建立的速度,但是基本上只有一个上限速度,就是CPU使用率100%的时候的速率,看一看就完事了。而且,我开一个客户端,达到300/s,开两个,2个600/s,3个,650/s,但是我在服务端的角度看,怎么统计这个事情,很难用程序统计然后把结果写到日志里,只能看一看日志,发现第二个客户端的ip了,就知道这个是从第二个客户端启动,速率发生了变化,未来画折线图的时候才多一条线。
后续的坑等做到后面了再写吧。