Netty测试

测试环境:

 

服务器 1台

OS: Red Hat Enterprise Linux Server release 5.4

CPU: 4xIntel(R) Xeon(R) CPU E5450 @ 3.00GHz

MEMORY:4G

客户端5台,配置同服务器

上述的测试环境都是采用的虚拟机,而且虚拟机性能不是很好,所以我主要是测连接数,其中有涉及到性能的地方不具可参考性。另外Comet需要关注的是它能支撑的连接数个数,而并非qps,当然qps也是我们需要考虑的性能点之一。

 

 

服务端环境配置

 

JDK版本1.6.0_18

Netty版本3.2.2

 

sudo vi /etc/sysctl.conf

加入如下配置:

 

Shell代码 复制代码 收藏代码
  1. net.core.somaxconn = 2048
  2. net.core.rmem_default = 262144
  3. net.core.wmem_default = 262144
  4. net.core.rmem_max = 16777216
  5. net.core.wmem_max = 16777216
  6. net.core.somaxconn = 10000
  7. net.core.netdev_max_backlog = 20000
  8. net.ipv4.tcp_rmem = 71681126416777216
  9. net.ipv4.tcp_wmem = 71681126416777216
  10. net.ipv4.tcp_mem = 78643220971523145728
  11. net.ipv4.tcp_max_syn_backlog = 16384
  12. net.ipv4.tcp_fin_timeout = 15
  13. net.ipv4.tcp_max_syn_backlog = 16384
  14. net.ipv4.tcp_tw_reuse = 1
  15. net.ipv4.tcp_tw_recycle = 1
  16. net.ipv4.tcp_max_orphans = 131072
  17. net.ipv4.tcp_max_tw_buckets=180000
  18. fs.file-max = 1000000
net.core.somaxconn = 2048
net.core.rmem_default = 262144
net.core.wmem_default = 262144
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.core.somaxconn = 10000
net.core.netdev_max_backlog = 20000

net.ipv4.tcp_rmem = 7168 11264 16777216
net.ipv4.tcp_wmem = 7168 11264 16777216
net.ipv4.tcp_mem = 786432 2097152 3145728
net.ipv4.tcp_max_syn_backlog = 16384
net.ipv4.tcp_fin_timeout = 15
net.ipv4.tcp_max_syn_backlog = 16384
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_tw_recycle = 1
net.ipv4.tcp_max_orphans = 131072
net.ipv4.tcp_max_tw_buckets=180000
fs.file-max = 1000000

编辑完成后执行如下命令让配置生效

sudo /sbin/sysctl -p

 

sudo vi /etc/security/limits.conf

找到hard nofile和soft nofile配置信息,修改为如下:

Shell代码 复制代码 收藏代码
  1. * hard nofile 1000000
  2. * soft nofile 1000000
*  hard nofile 1000000
*  soft nofile 1000000

 

客户端环境配置

 

由于系统默认参数,自动分配的端口数有限,是从32768到61000,所以我们需要更改客户端/etc/sysctl.conf的参数:

 

Shell代码 复制代码 收藏代码
  1. net.ipv4.ip_local_port_range = 102465535
net.ipv4.ip_local_port_range = 1024 65535

 

编辑完成后执行如下命令让配置生效

sudo /sbin/sysctl -p

客户端程序是基于libevent写的一个测试程序,不断的建立新的连接请求。客户端与服务端需要建立大量的socket,所以我们需要调速一下最大文件描述符。客户端,需要创建六万多个socket,我设置最大为十万,在 /etc/security/limits.conf 中添加

 

Shell代码 复制代码 收藏代码
  1. * hard nofile 1000000
  2. * soft nofile 1000000
*  hard nofile 1000000
*  soft nofile 1000000

 

 

java的内存调优

 

对于java的内存管理,肯定离不开GC的调优。长连接是一个长期占用内存的一种应用,直到连接中断才会被释放。如果按照传统的方式来分配Eden、S0、S1是存在问题的,如果按照默认的配置Eden空间大于Survivor,那么在进行GC的时候由于长连接所占用的内存并不释放,导致Survivor空间无法容纳,收集器会将无法容纳的数据丢入到Old区。

 

为了验证这个情况作了一个测试,java的配置参数如下:

-server -Xms1G -Xmx1G -XX:PermSize=64m -XX:+UseParallelGC -XX:+UseParallelGC -verbose:gc -XX:+PrintGCDetails

运行之后jstat日志信息如下,关注其中的红色部分,进行minor GC的时候由于S1的空间不够容纳,收集器将无法容纳的数据丢入到Old区,如果持续下去,会频繁进行Full GC,这是我们不想看到的。

 

 

S0 S1 E O P YGC YGCT FGC FGCT GCT

0.00 0.00 98.00 0.00 8.78 0 0.000 0 0.000 0.000

0.00 99.93 9.94 9.20 8.78 1 0.195 0 0.000 0.195

0.00 99.93 9.94 9.20 8.78 1 0.195 0 0.000 0.195

0.00 99.93 9.94 9.20 8.78 1 0.195 0 0.000 0.195

0.00 99.93 9.94 9.20 8.78 1 0.195 0 0.000 0.195

0.00 99.93 9.94 9.20 8.78 1 0.195 0 0.000 0.195

0.00 99.93 9.94 9.20 8.78 1 0.195 0 0.000 0.195

0.00 99.93 13.84 9.20 8.78 1 0.195 0 0.000 0.195

0.00 99.93 25.08 9.20 8.78 1 0.195 0 0.000 0.195

0.00 99.93 37.62 9.20 8.78 1 0.195 0 0.000 0.195

0.00 99.93 48.86 9.20 8.78 1 0.195 0 0.000 0.195

0.00 99.93 51.46 9.20 8.78 1 0.195 0 0.000 0.195

0.00 99.93 62.70 9.20 8.78 1 0.195 0 0.000 0.195

0.00 99.93 66.60 9.20 8.78 1 0.195 0 0.000 0.195

0.00 99.93 77.84 9.20 8.78 1 0.195 0 0.000 0.195

0.00 99.93 79.14 9.20 8.78 1 0.195 0 0.000 0.195

0.00 99.93 90.39 9.20 8.78 1 0.195 0 0.000 0.195

99.93 0.00 7.17 26.40 8.78 2 0.511 0 0.000 0.511

 

实际在线上的应用场景,连接每隔一段时间就会关闭。那么仍到Old区的内存几乎每次都能够完全回收,但是交给Old区来回收实在是太重了,如何避免频繁的Full GC是我们需要关注的。

 

接下来另外的一种配置方式,淘宝韩彰分享过这种做法。加大Survivor区,让Survivor区的空间和Eden空间一样大,这样可以保证在minor GC的时候数据不会放入到Old区。下一次执行minor GC的时候,先前的Survivor区的数据可以完全回收掉。

PS:前提是minor GC周期必须大于一个连接的生命周期,比如一个连接每隔30s会关闭,那么两次minor GC的时间间隔必须大于30s。

 

测试

 

最后,为了测试Netty支持最大的连接数,我们配置如下:

 

Shell代码 复制代码 收藏代码
  1. -server -Xms4G -Xmx4G -XX:NewSize=3584m -XX:PermSize=64m -XX:SurvivorRatio=1 -XX:+UseParallelGC -XX:-UseAdaptiveSizePolicy
-server -Xms4G -Xmx4G -XX:NewSize=3584m -XX:PermSize=64m -XX:SurvivorRatio=1 -XX:+UseParallelGC -XX:-UseAdaptiveSizePolicy

Eden、S0、S1各1G,Old512M,UseParallelGC回收机制,加入UseAdaptiveSizePolicy不允许回收器自动调整Eden和Survivor区大小。

 

启动服务端的comet进程后,初始的对象占用 167.26MB。然后启动客户端连接到服务端,每个客户端建立6w个连接。当客户端的连接数达到 6w左右的时候就不能再连接了。

 

执行server端的dmesg命令,发现存在如下信息:

 

Shell代码 复制代码 收藏代码
  1. ip_conntrack: table full, dropping packet.
ip_conntrack: table full, dropping packet.

再执行如下命令

sudo cat /proc/sys/net/ipv4/ip_conntrack_max

是65322,看来是最大连接跟踪值开得较小了。

 

接下来执行sudo vi /etc/sysctl.conf 进行编辑,加入如下配置:

 

Shell代码 复制代码 收藏代码
  1. net.ipv4.ip_conntrack_max = 1000000 //设置最大连接跟踪值
  2. net.ipv4.netfilter.ip_conntrack_max=1000000
  3. net.ipv4.netfilter.ip_conntrack_tcp_timeout_time_wait=120
  4. net.ipv4.netfilter.ip_conntrack_tcp_timeout_close_wait=60
  5. net.ipv4.netfilter.ip_conntrack_tcp_timeout_fin_wait=120
net.ipv4.ip_conntrack_max = 1000000   //设置最大连接跟踪值
net.ipv4.netfilter.ip_conntrack_max=1000000 
net.ipv4.netfilter.ip_conntrack_tcp_timeout_time_wait=120 
net.ipv4.netfilter.ip_conntrack_tcp_timeout_close_wait=60 
net.ipv4.netfilter.ip_conntrack_tcp_timeout_fin_wait=120

再从新进行测试,现在连接已经可以突破6w了。

 

 

当生成12万个连接后,内存占用1194.75MB。

也就是说每个连接占用8.8k = (1194.75-167.26)*1024/120000

这只是纯粹的连接,还没有涉及到数据的传输,一旦涉及到数据的传输,每个连接占用的内存肯定会超过8.8k

 

当超过12万之后java进程会进行一次minor GC,耗时1.11秒(用的是虚拟机,线上环境应该会好上几倍)

具体信息如下:

398.120: [GC [PSYoungGen: 1223424K->504705K(2446720K)] 1223424K->504705K(2971008K), 1.1087630 secs] [Times: user=2.45 sys=0.81, real=1.11 secs]

 

持续增加连接,当增加到26万个连接系统仍然表现稳定。

 

当连接数达到27万左右的时候java进程进行了第二次minor GC,由于是一直保持着的长连接,数据是不会被释放的,此时杯具的事情发生了:

 

下面是第二次minor GC的信息

 

S0 S1 E O P YGC YGCT FGC FGCT GCT

0.00 41.26 100.00 0.00 8.81 1 1.109 0 0.000 1.109

5.51 41.26 100.00 0.00 8.81 2 1.109 0 0.000 1.109

21.86 41.26 100.00 0.00 8.81 2 1.109 0 0.000 1.109

40.32 41.26 100.00 0.00 8.81 2 1.109 0 0.000 1.109

56.11 41.26 100.00 0.00 8.81 2 1.109 0 0.000 1.109

67.12 41.26 100.00 0.00 8.81 2 1.109 0 0.000 1.109

78.00 41.26 100.00 0.00 8.81 2 1.109 0 0.000 1.109

80.87 41.26 100.00 0.00 8.81 2 1.109 0 0.000 1.109

80.92 41.26 100.00 0.00 8.81 2 1.109 0 0.000 1.109

80.92 41.26 100.00 0.00 8.81 2 1.109 0 0.000 1.109

.....

86.05 0.00 29.31 0.00 8.90 2 588.199 0 0.000 588.199

 

最终耗时587s,不可想象啊!

 

Log代码 复制代码 收藏代码
  1. 1777.982: [GC [PSYoungGen: 1728129K->1052673K(2446720K)] 1728129K->1052673K(2971008K), 587.0906510 secs]
1777.982: [GC [PSYoungGen: 1728129K->1052673K(2446720K)] 1728129K->1052673K(2971008K), 587.0906510 secs]

结论:

上述测试场景下最多能够支撑26万左右的长连接。超过26万就会出现上述第二次minor GC的情况,可用性无法得到保障。

 

在真实场景下,并不是真正的保持长连接状态,当连接了一定的时间,连接会关闭掉。假如设定每个连接每隔30s会关闭,那么你只需要保证minior GC的周期大于30s就可以避免上述第二次minor GC的情况。因为放入到S1中的数据一定能在下一个GC周期内全部释放掉。

 

目前的测试场景还不能完全反映和还原线上真实场景,真实场景下有着各种复杂的情况,数据传输对内存的占用也会不一样。线上的连接数肯定要打折扣。

 

如果 minor GC 的周期大于30s,保守估计一个netty进程大概能支持10万左右长连接(1G的Eden空间的能容纳12万个连接)。

 

另外,感谢淘宝 李子 在整个测试过程中予以的帮助。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值