189 8069 5689

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


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

创新互联是一家集网站建设,青田企业网站建设,青田品牌网站建设,网站定制,青田网站建设报价,网络营销,网络优化,青田网站推广为一体的创新建站企业,帮助传统企业提升企业形象加强企业竞争力。可充分满足这一群体相比中小企业更为丰富、高端、多元的互联网需求。同时我们时刻保持专业、时尚、前沿,时刻以成就客户成长自我,坚持不断学习、思考、沉淀、净化自己,让我们为更多的企业打造出实用型网站。测试服务器配置

运行在VMWare Workstation 9中,64位Centos 6.2系统,分配14.9G内存左右,4核。
已安装有Java7版本:

javaversion"1.7.0_21" Java(TM)SERuntimeEnvironment(build1.7.0_21-b11) JavaHotSpot(TM)64-BitServerVM(build23.21-b01,mixedmode)

测试端

测试端和以前一样的程序,翻看前几篇博客就可以看到client5.c的源码。

在/etc/sysctl.conf中添加如下配置:

fs.file-max=1048576 net.ipv4.ip_local_port_range=102465535 net.ipv4.tcp_mem=78643220971523145728 net.ipv4.tcp_rmem=4096409616777216 net.ipv4.tcp_wmem=4096409616777216 net.ipv4.tcp_tw_reuse=1 net.ipv4.tcp_tw_recycle=1

服务器程序

这次也是很简单呐,没有业务功能,客户端HTTP请求,服务端输出chunked编码内容。

入口HttpChunkedServer.java: 唯一的自定义处理器HttpChunkedServerHandler.java: 启动脚本start.sh 达到100万并发连接时的一些信息

每次服务器端达到一百万个并发持久连接之后,然后关掉测试端程序,断开所有的连接,等到服务器端日志输出在线用户为0时,再次重复以上步骤。在这反反复复的情况下,观察内存等信息的一些情况。以某次断开所有测试端为例后,当前系统占用为(设置为 list_free_1):

totalusedfreesharedbufferscached Mem:1518977367453018120 -/+buffers/cache:75977592 Swap:40959483147

通过top观察,其进程相关信息

PIDUSERPRNIVIRTRESSHRS%CPU%MEMTIME+COMMAND 4925root2008206m4.3g2776S0.328.850:18.66java

在启动脚本start.sh中,我们设置堆内存为6G。

ps aux|grep java命令获得信息:

root492538.028.884034444484764?Sl15:2650:18java-server...HttpChunkedServer8000

RSS占用内存为4484764K/1024K=4379M

然后再次启动测试端,在服务器接收到 online user 1023749时, ps aux|grep java内容为:

root492543.628.484034444422824?Sl15:2662:53java-server...

查看当前网络信息统计

ss-s Total:1024050(kernel1024084) TCP:1023769(estab1023754,closed2,orphaned0,synrecv0,timewait0/0),ports12 TransportTotalIPIPv6 *1024084-- RAW000 UDP761 TCP1023767121023755 INET1023774181023756 FRAG000

通过top查看一下

top-p4925 top-17:51:30up3:02,4users,loadaverage:1.03,1.80,1.19 Tasks:1total,0running,1sleeping,0stopped,0zombie Cpu0:0.9%us,2.6%sy,0.0%ni,52.9%id,1.0%wa,13.6%hi,29.0%si,0.0%st Cpu1:1.4%us,4.5%sy,0.0%ni,80.1%id,1.9%wa,0.0%hi,12.0%si,0.0%st Cpu2:1.5%us,4.4%sy,0.0%ni,80.5%id,4.3%wa,0.0%hi,9.3%si,0.0%st Cpu3:1.9%us,4.4%sy,0.0%ni,84.4%id,3.2%wa,0.0%hi,6.2%si,0.0%st Mem:15554336ktotal,15268728kused,285608kfree,3904kbuffers Swap:4194296ktotal,1082592kused,3111704kfree,37968kcached PIDUSERPRNIVIRTRESSHRS%CPU%MEMTIME+COMMAND 4925root2008206m4.2g2220S3.328.462:53.66java

四核都被占用了,每一个核心不太平均。这是在虚拟机中得到结果,可能真实服务器会更好一些。 因为不是CPU密集型应用,CPU不是问题,无须多加关注。

系统内存状况

free-m totalusedfreesharedbufferscached Mem:15189149262630556 -/+buffers/cache:14864324 Swap:409510573038

物理内存已经无法满足要求了,占用了1057M虚拟内存。

查看一下堆内存情况

jmap-heap4925 AttachingtoprocessID4925,pleasewait... Debuggerattachedsuccessfully. Servercompilerdetected. JVMversionis23.21-b01 usingparallelthreadsinthenewgeneration. usingthread-localobjectallocation. ConcurrentMark-SweepGC HeapConfiguration: MinHeapFreeRatio=40 MaxHeapFreeRatio=70 MaxHeapSize=6442450944(6144.0MB) NewSize=629145600(600.0MB) MaxNewSize=629145600(600.0MB) OldSize=5439488(5.1875MB) NewRatio=2 SurvivorRatio=1 PermSize=52428800(50.0MB) MaxPermSize=52428800(50.0MB) G1HeapRegionSize=0(0.0MB) HeapUsage: NewGeneration(Eden+1SurvivorSpace): capacity=419430400(400.0MB) used=308798864(294.49354553222656MB) free=110631536(105.50645446777344MB) 73.62338638305664%used EdenSpace: capacity=209715200(200.0MB) used=103375232(98.5863037109375MB) free=106339968(101.4136962890625MB) 49.29315185546875%used FromSpace: capacity=209715200(200.0MB) used=205423632(195.90724182128906MB) free=4291568(4.0927581787109375MB) 97.95362091064453%used ToSpace: capacity=209715200(200.0MB) used=0(0.0MB) free=209715200(200.0MB) 0.0%used concurrentmark-sweepgeneration: capacity=5813305344(5544.0MB) used=4213515472(4018.321487426758MB) free=1599789872(1525.6785125732422MB) 72.48054631000646%used PermGeneration: capacity=52428800(50.0MB) used=5505696(5.250640869140625MB) free=46923104(44.749359130859375MB) 10.50128173828125%used 1439internedStringsoccupying110936bytes.

老生代占用内存为72%,较为合理,毕竟系统已经处理100万个连接。

再次断开所有测试端,看看系统内存(free -m)

totalusedfreesharedbufferscached Mem:1518977237466013120 -/+buffers/cache:75897599 Swap:40959503145

记为 list_free_2。

list_free_1和 list_free_2两次都释放后的内存比较结果,系统可用物理已经内存已经降到7589M,先前可是7597M物理内存。
总之,我们的JAVA测试程序在内存占用方面已经,低需要7589 + 950 = 8.6G内存为低需求内存吧。

GC日志

我们在启动脚本处设置的一大串参数,到底是否达到目标,还得从gc日志处获得具体效果,推荐使用 GCViewer。

GC事件概览:

其它:

总之:

只进行了一次Full GC,代价太高,停顿了12秒。

PartNew成为了停顿大户,导致整个系统停顿了41秒之久,不可接受。

当前JVM调优喜忧参半,还得继续努力等

小结

Java与与Erlang、C相比,比较麻烦的事情,需要在程序一开始就得准备好它的堆栈到底需要多大空间,换个说法就是JVM启动参数设置堆内 存大小,设置合适的垃圾回收机制,若以后程序需要更多内存,需停止程序,编辑启动参数,然后再次启动。总之一句话,就是麻烦。单单JVM的调优,就得持续 不断的根据检测、信息、日志等进行适当微调。

JVM需要提前指定堆大小,相比Erlang/C,这可能是个麻烦

GC(垃圾回收),相对比麻烦,需要持续不断的根据日志、JVM堆栈信息、运行时情况进行JVM参数微调

设置一个连接目标,多次测试达到顶峰,然后释放所有连接,反复观察内存占用,获得一个较为合适的系统运行内存值

Eclipse Memory Analyzer结合jmap导出堆栈DUMP文件,分析内存泄漏,还是很方便的

想修改运行时内容,或者称之为热加载,默认不可能

真实机器上会有更好的反映

吐槽一下:
JAVA OSGI,相对比Erlang来说,需要人转换思路,不是那么原生的东西,总是有些别扭,社区或商业公司对此的修修补补,不过是实现一些面向对象所不具备的热加载的企业特性。

测试源代码, 下载just_test。


网站标题:100万并发连接服务器笔记之JavaNetty处理1M连接会怎么样
新闻来源:http://jkwzsj.com/article/cjgcjs.html