1:C1000K 实战,基于 Libev

最近公司接到一个新项目,是基于Socket通信的GPS实时记录系统。实际设备台数是在20W左右,但是自己并没有对实时性要求这么高的高并发服务器软件设计经验,只能硬着头皮先进行测试开发试一下。
技术选型的话,当时是直接想使用Windows下面的IOCP,因为自己对C#这块比较熟悉,不过经过测试,在WindowsServer上面的测试结果并不理想,可能是自己水平不到家吧(笑)。关于高性能的IOCP编程大家可以参考这位仁兄的文章,写得很不错,而且也有实例:(C#高性能大容量SOCKET并发(一):IOCP完成端口例子介绍)
之后查阅了相关的技术资料和各类博客,发现Linux更适合做此类系统,所以选择了CentOS 7.x 64bit作为开发系统,高并发主要在于对Socket的监听一定要及时,所以Epoll是最好的选择。但是博主并不想直接再写一次Epoll了,相关Epoll进行高并发编程的可以参考**这篇文章**很不错的实例。所以我直接选择了Libev库作为底层的事件库,进行事件监听。

首先我们要实现C1000K的话,得对Linux的内核参数进行调优,怎么调?
第一步,调整系统单个进程最大打开的文件句柄数量:

vim /etc/security/limits.conf

在最末尾加入:

* soft nofile 1048576
* hard nofile 1048576

因为Linux下面一皆文件,即便是我们的Socket网络套接字也是属于文件句柄这个范畴的,默认大小可以通过ulimit -n查看,默认是1024个,对于我们百万并发的服务器来说显然是不够的。
这里的星号(*)代表的是对所有用户生效,当然你也可以手动指定用户,soft是警告阀值,而hard则是实际生效的阀值,在这里我们为了方便直接都改成一样的就可以了。

第二步,设置系统所有进程最多同时打开的句柄数量限制:

echo "fs.file-max = 1048576" >> /etc/sysctl.conf

之后执行以下命令使其生效:

sysctl -p

第三步,TCP参数调优:

echo "net.ipv4.tcp_mem = 786432 2097152 3145728" >> /etc/sysctl.conf
echo "net.ipv4.tcp_rmem = 4096 4096 16777216" >> /etc/sysctl.conf
echo "net.ipv4.tcp_wmem = 4096 4096 16777216" >> /etc/sysctl.conf

tcp_rmem是TCP读取缓冲区,tcp_wmem是发送缓冲区,他们的单位都是字节,第一个是最小值,第二个是默认值,第三个是最大值,这里分别设置为4K,4K,16MB。
tcp_mem是TCP内存大小,单位是页,一页为4096字节。

  • low:当TCP使用了低于该值的内存页面数时,TCP不会考虑释放内存。
  • pressure:当TCP使用了超过该值的内存页面数量时,TCP试图稳定其内存使用,进入pressure模式,当内存消耗低于low值时则退出pressure状态。
  • high:允许所有tcp sockets用于排队缓冲数据报的页面量,当内存占用超过此值,系统拒绝分配socket,后台日志输出“TCP: too many of orphaned sockets”。
    一般情况下这些值是在系统启动时根据系统内存数量计算得到的。 根据当前tcp_mem最大内存页面数是1864896,当内存为(1864896*4)/1024K=7284.75M时,系统将无法为新的socket连接分配内存,即TCP连接将被拒绝。

实际测试环境中,据观察大概在99万个连接左右的时候(零头不算),进程被杀死,触发out of socket memory错误(dmesg命令查看获得)。每一个连接大致占用7.5K内存(下面给出计算方式),大致可算的此时内存占用情况(990000 * 7.5 / 1024K = 7251M)。

未完待续