100万并发连接服务器笔记之准备篇

标签: 并发 服务器 笔记 | 发表时间:2013-04-09 09:50 | 作者:nieyong
出处:http://www.blogjava.net/

前言

测试一个非常简单服务器如何达到100万(1M=1024K连接)的并发连接,并且这些连接一旦连接上服务器,就不会断开,一直连着。
环境受限,没有服务器,刚开始都是在自己的DELL笔记本上测试,凭借16G内存,和优秀的vmware workstation虚拟机配合,另外还得外借别人虚拟机使用,最终还得搭上两台2G内存的台式机(安装centos),最终才完成1M并发连接任务。

  • 测试程序也很简陋,一个C语言所写服务器程序,没有任何业务存在,收到请求后发送一些头部,不断开连接
  • 测试端程序也是使用C语言所写,发送请求,然后等待接收数据,仅此而已
  • 服务器端/测试端内存都受限(8G不够使用),要想完成1024K的目标,需要放弃一些东西,诸如业务不是那么完整
  • 一台分配10G内存Centos服务器,两台分配6G内存Centos测试端,两台2G内存Centos测试端
  • 假如热心的您可以提供丰富的服务器资源,那就再好不过了。
  • 理论上200万的并发连接(IO密集型),加上业务,40G-50G的内存大概能够保证

说明

以前也做过类似的工作,量不大,没记录下来,一些压力测试和调优,随着时间流逝,早已忘记。这次是从零开始,基本上所有过程都会记录,一步一步,每一步都会遇到问题,并且给出相关解决问题的方法,最终完成目标。
为了方便,服务器端程序和客户端测试程序,都是使用C语言,不用像JAVA一样需要预先指定内存,感觉麻烦。使用较为原始的语言来写,可以避免不必要的调优工作。这中间,可能会穿插Java代码的思考方式。

可能需要懂点Linux,C,Java,假如您有更好的做法,或者建议,请直接告知,谢谢。

Linux系统

测试端和服务器端都选用较为熟悉的64位Centos 6.4,32位系统最多支持4G内存,太受限。IO密集型应用,对CPU要求不是很高。另外服务器确保安装上gcc,那就可以开工了。
所有端系统一旦安装完之后,默认不做任何设置。

服务器端程序

服务器端程序依赖 libev框架,需要提前编译,然后存放到相应位置。下面是具体服务器端代码:

编译

  gcc server.c -o server ../include/libev.a -lm

运行

  ./server -p 8000

在源码中默认指定了8000端口,可以通过-p进行指定新的端口。 开启了8000端口进行监听请求,http协议处理类似于htmlfile chunked块编码传输。

测试服务器端程序

测试程序使用libevent框架,因其使用简单,提供丰富易用接口,但需要提前下载,手动安装:

  wget https://github.com/downloads/libevent/libevent/libevent-2.0.21-stable.tar.gz
tar xvf libevent-2.0.21-stable.tar.gz
cd libevent-2.0.21-stable
./configure --prefix=/usr
make
make install

注意make和make install需要root用户。

测试端程序

client1.c 源码:

备注:这部分代码参考了 A Million-user Comet Application with Mochiweb, Part 3 ,根据需要有所修改。

编译

  gcc -o client1 client1.c -levent

运行

  ./client1

可能在64位系统会遇到找不到 libevent-2.0.so.5情况,需要建立一个软连接

  ln -s /usr/lib/libevent-2.0.so.5 /lib64/libevent-2.0.so.5

即可自动连接IP地址为192.168.190.133:8000的服务器端应用。

第一个遇到的问题:文件句柄受限

测试端程序输出

看看测试端程序client1输出的错误信息:

  Chunks: 798 Bytes: 402990 Closed: 0
Req: 192.168.190.133 -/test/900
Req: 192.168.190.133 -/test/1000
Chunks: 998 Bytes: 503990 Closed: 0
[warn] socket: Too many open files
[warn] socket: Too many open files
[warn] socket: Too many open files

服务器端程序输出

服务器端最后一条日志为

  online user 1018

两边都遇到了文件句柄打开的情况。
在服务器端查看已经连接,并且端口号为8000的所有连接数量:

  netstat -nat|grep -i "8000"|wc -l    
1019

但与服务器端输出数量对不上,增加所有已经建立连接的选项:

  netstat -nat|grep -i "8000"|grep ESTABLISHED|wc -l    
1018

那么剩下的一条数据到底是什么呢?

  netstat -nat|grep -i "8000"|grep -v ESTABLISHED
tcp 0 0 0.0.0.0:8000 0.0.0.0:* LISTEN 

也就是server.c监听的端口,数量上对的上。

在测试服务器端,查看测试进程打开的文件句柄数量

  lsof -n|grep client1|wc -l
1032

再次执行

  ulimit -n
1024

也是就是client1应用程序共打开了 1032个文件句柄,而不是 1024,为什么?
把当前进程所有打开的文件句柄保存到文件中,慢慢研究 lsof -n|grep client1 > test connfinfo.txt

导出的文件可以参考: https://gist.github.com/yongboy/5260773
除了第一行,我特意添加上供友善阅读的头部列定义,也就是1032行信息,但是需要注意头部:

  
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
client1 3088 yongboy cwd DIR 253,0 4096 800747 /home/yongboy/workspace/c_socket.io_server/test
client1 3088 yongboy rtd DIR 253,0 4096 2 /test_conn
client1 3088 yongboy txt REG 253,0 9697 799991 /home/yongboy/workspace/c_socket.io_server/test/test_conn_1
client1 3088 yongboy mem REG 253,0 156872 50404 /lib64/ld-2.12.so
client1 3088 yongboy mem REG 253,0 1922152 78887 /lib64/libc-2.12.so
client1 3088 yongboy mem REG 253,0 145720 76555 /lib64/libpthread-2.12.so
client1 3088 yongboy mem REG 253,0 47064 69491 /lib64/librt-2.12.so
client1 3088 yongboy mem REG 253,0 968730 26292 /usr/lib/libevent-2.0.so.5.1.9
client1 3088 yongboy 0u CHR 136,2 0t0 5 /dev/pts/2
client1 3088 yongboy 1u CHR 136,2 0t0 5 /dev/pts/2
client1 3088 yongboy 2u CHR 136,2 0t0 5 /dev/pts/2
client1 3088 yongboy 3u REG 0,9 0 4032 anon_inode
client1 3088 yongboy 4u unix 0xffff88007c82f3c0 0t0 79883 socket
client1 3088 yongboy 5u unix 0xffff880037c34380 0t0 79884 socket
client1 3088 yongboy 6u IPv4 79885 0t0 TCP 192.168.190.134:58693->192.168.190.133:irdmi (ESTABLISHED)
client1 3088 yongboy 7u IPv4 79889 0t0 TCP 192.168.190.134:58694->192.168.190.133:irdmi (ESTABLISHED)
client1 3088 yongboy 8u IPv4 79891 0t0 TCP 192.168.190.134:58695->192.168.190.133:irdmi (ESTABLISHED)
client1 3088 yongboy 9u IPv4 79893 0t0 TCP 192.168.190.134:58696->192.168.190.133:irdmi (ESTABLISHED)

可以看到文件句柄是从0u开始,0u上面的8个(5个mem + 3个启动)进程,1032 - 8 = 1024个文件句柄,这样就和系统限制的值吻合了。

root用户编辑/etc/security/limits.conf文件添加:

  * soft nofile 1048576
* hard nofile 1048576
  • soft是一个警告值,而hard则是一个真正意义的阀值,超过就会报错。
  • soft 指的是当前系统生效的设置值。hard 表明系统中所能设定的最大值
  • nofile - 打开文件的最大数目
  • 星号表示针对所有用户,若仅针对某个用户登录ID,请替换星号

注意:
1024K x 1024 = 1048576K = 1M,1百万多一点。

备注:测试端和服务器端都需要作此设置,保存退出,然后reboot即可生效。

第一个问题,就这样克服了。再次运行 /client1测试程序,就不会出现受打开文件句柄的限制。但大概在测试端打开对外28200个端口时,会出现程序异常,直接退出。

段错误

这个也是程序没有处理端口不够用的异常,但可以通过增加端口进行解决。

备注: 但测试端单机最多只能打开6万多个连接,是一个问题,如何克服,下一篇解决此问题,并且还会遇到文件句柄的受限问题。



nieyong 2013-04-09 09:50 发表评论

相关 [并发 服务器 笔记] 推荐:

100万并发连接服务器笔记之准备篇

- - BlogJava-首页技术区
测试一个非常简单服务器如何达到100万(1M=1024K连接)的并发连接,并且这些连接一旦连接上服务器,就不会断开,一直连着. 环境受限,没有服务器,刚开始都是在自己的DELL笔记本上测试,凭借16G内存,和优秀的vmware workstation虚拟机配合,另外还得外借别人虚拟机使用,最终还得搭上两台2G内存的台式机(安装centos),最终才完成1M并发连接任务.

100万并发连接服务器笔记之测试端就绪

- - BlogJava-首页技术区
测试端程序需要增加绑定本机IP和本地端口的功能,以尽可能的向外发出更多的tcp请求. 需要对client1.c重构,增加参数传递. 下面是client2.c的代码. 若不指定端口,系统会随机挑选没有使用到的端口,可以节省些心力. -h 要连接的服务器IP地址. -m 本机IP地址需要绑定的随机端口数量.

100万并发连接服务器笔记之1M并发连接目标达成

- - BlogJava-首页技术区
第四个遇到的问题:tcp_mem. 在服务端,连接达到一定数量,诸如50W时,有些隐藏很深的问题,就不断的抛出来. 通过查看 dmesg命令查看,发现大量 TCP: too many of orphaned sockets错误,也很正常,下面到了需要调整tcp socket参数的时候了. 第一个需要调整的是tcp_rmem,即TCP读取缓冲区,单位为字节,查看默认值.

100万并发连接服务器笔记之Erlang完成1M并发连接目标

- - BlogJava-首页技术区
使用Erlang语言也写一个测试和前面大同小异的测试,在100万个并发连接用户情况下,就是想观察一下极显情况下的表现. 这个测试使用了优秀的Erlang界的明星框架. cowboy,加单易用的接口,避免了我们对HTTP栈再次进行闭门造车. 运行在VMWare Workstation 9中,64位Centos 6.4系统,分配14.9G内存左右,双核4个线程,服务器安装Erlang/OTP R16B,最新版本支持异步代码热加载,很赞.

100万并发连接服务器笔记之Java Netty处理1M连接会怎么样

- - BlogJava-首页技术区
每一种该语言在某些极限情况下的表现一般都不太一样,那么我常用的Java语言,在达到100万个并发连接情况下,会怎么样呢,有些好奇,更有些期盼. netty NIO框架(netty-3.6.5.Final),封装的很好,接口很全面,就像它现在的域名 netty.io,专注于网络IO. 整个过程没有什么技术含量,浅显分析过就更显得有些枯燥无聊,准备好,硬着头皮吧.

笔记: Xen VM 里面的 MySQL 服务器优化

- - #raynix's notes
我一直都对公司 Xen VM 的数据库服务器不满, 因为实在是太慢了. 但是几百个 GB 的商业数据我可不敢动, 于是先在测试服务器上证实一下我的想法. 硬盘就是普通的 SATA 7200RPM, VM 用的是 LVM 分区. 然后我用之前写的一个小程序做批量更新, 32K 记录. 缺省配置下, 运行时长达到24分钟, 而优化后则只需要27秒.

java并发编程实践学习笔记

- - zzm
    原子操作:原子为不可再分操作.    Violation :可见关键字.    Synchronized:内部隐示锁 .    ReentrantLock:显示锁 .    ReentrantReadWriteLock:读写锁 . jmm(java内存模型):. 线程对所有变量的操作都是在工作内存中进行,线程之间无法相互直接访问,变量传递 均需要通过主存完成.

笔记

- 毛毛 - 游戏人生
我关于写代码的一些琐碎的看法. 之前没有把 Paul Graham 的 <黑客与画家> 一书读完, 上周就从同事那里把书带回家, 也一直没读, 到这周才有时间读完. 很久没有更新了 (一看时间, 整整 5 个月), 顺便把这篇写了几个月的感想放出来.. 这本书前面 8 章讲述的内容, 大多是我并不太感兴趣的, 比如财富, 比如创业.

kernel.org服务器遭入侵

- Lamo - Solidot
kernel.org网站首页发布公告,声称多台服务器在本月初(8月12日前)遭黑客攻击,他们在8月28日发现了入侵. 入侵者利用一位用户凭证获得了服务器根访问权限,他们正在调查黑客是如何提升权限的;系统启动脚本被加入了一个木马启动文件;ssh相关文件被修改. kernel.org声称,他们相信Linux kernel源代码库未受影响,因为git分布式版本控制系统的特性决定了它可以很容易注意到代码变化.

Ubuntu下赌ARM服务器

- Tim - Solidot
今日无数手机平板使用的低能耗处理器能否撑起未来的服务器市场. Canonical计划推出支持ARM架构的Ubuntu服务器版本. Ubuntu Linux并不是x86服务器市场的重量级选手,Red Hat才是. 但通过与ARM合作打造ARM服务器,Canonical正努力赢得更多市场份额. 计划于2011年10月发布的Ubuntu Server 11.10,将同步推出支持x86、x86-64和ARM架构的版本.