单机百万连接调优和Netty应用级别调优
单机百万连接调优
准备两台Linux服务器,一个充当服务端,一个充当客户端。
服务端操作系统:CentOS 7配置:4核8GIP:192.168.118.138
客户端操作系统:CentOS 7配置:4核8GIP:192.168.118.139
服务端和客户端均要配置java环境,基于jdk1.8。如何模拟百万连接#
如果服务端只开一个端口,客户端连接的时候,端口号是有数量限制的(非root用户,从1024到65535,大约6w),所以服务端开启一个端口,客户端和服务端的连接最多6w个左右。
为了模拟单机百万连接,我们在服务端开启多个端口,例如8000~8100,一共100个端口,客户端还是6w的连接,但是可以连接服务端的不同端口,所以就可以模拟服务端百万连接的情况。准备服务端程序#
服务端程序的主要逻辑是:
绑定8000端口一直到8099端口,一共100个端口,每2s钟统计一下连接数。
channelActive触发的时候,连接+1, channelInactive触发的时候,连接-1。
代码见:Server.java准备客户端程序#
客户端程序的主要逻辑是:
循环连接服务端的端口(从8000一直到8099)。
代码见:Client.java
准备好客户端和服务端的代码后,打包成Client.jar和Server.jar并上传到客户端和服务端的/data/app目录下。打包配置参考pom.xml
服务端和客户端在/data/app下分别准备两个启动脚本,其中服务端准备的脚本为startServer.sh, 客户端准备的脚本为startClient.sh,内容如下:
startServer.shjava -jar server.jar -Xms6.5g -Xmx6.5g -XX:NewSize=5.5g -XX:MaxNewSize=5.5g -XX:MaxDirectMemorySize=1g
startClient.shjava -jar client.jar -Xms6.5g -Xmx6.5g -XX:NewSize=5.5g -XX:MaxNewSize=5.5g -XX:MaxDirectMemorySize=1g
脚本文件见:startServer.sh 和 startClient.sh
先启动服务端cd /data/app/ ./startServer.sh
查看日志,待服务端把100个端口都绑定好以后。
在启动客户端cd /data/app/ ./startClient.sh
然后查看服务端日志,服务端在支撑了3942个端口号以后,报了如下错误:Caused by: java.io.IOException: Too many open files at sun.nio.ch.FileDispatcherImpl.init(Native Method) at sun.nio.ch.FileDispatcherImpl.(FileDispatcherImpl.java:35) 突破局部文件句柄限制#
使用ulimit -n命令可以查看一个jvm进程最多可以打开的文件个数,这个是局部文件句柄限制,默认是1024,我们可以修改这个值vi /etc/security/limits.conf
增加如下两行* hard nofile 1000000 * soft nofile 1000000
以上配置表示每个进程可以打开的最大文件数是一百万。突破全局文件句柄限制#
除了突破局部文件句柄数限制,还需要突破全局文件句柄数限制,修改如下配置文件vi /proc/sys/fs/file-max
将这个数量修改为一百万echo 1000000 > /proc/sys/fs/file-max
通过这种方式修改的配置在重启后失效,如果要使重启也生效,需要修改如下配置vi /etc/sysctl.conf
在文件末尾加上fs.file-max=1000000
服务端和客户端在调整完局部文件句柄限制和全局文件句柄限制后,再次启动服务端,待端口绑定完毕后,启动客户端。
查看服务端日志,可以看到,服务端单机连接数已经达到百万级别。..... connections: 434703 connections: 438238 connections: 441195 connections: 444082 connections: 447596 ..... connections: 920435 connections: 920437 connections: 920439 connections: 920442 connections: 920443 connections: 920445 ..... Netty应用级别调优#场景#
服务端接受到客户端的数据,进行一些相对耗时的操作(比如数据库查询,数据处理),然后把结果返回给客户端。模拟耗时操作#
在服务端,模拟通过sleep方法来模拟耗时操作,规则如下:在90.0%情况下,处理时间为1ms在95.0%情况下,处理时间为10ms在99.0%情况下,处理时间为100ms在99.9%情况下,处理时间为1000ms
代码如下protected Object getResult(ByteBuf data) { int level = ThreadLocalRandom.current().nextInt(1, 1000); int time; if (level <= 900) { time = 1; } else if (level <= 950) { time = 10; } else if (level <= 990) { time = 100; } else { time = 1000; } try { Thread.sleep(time); } catch (InterruptedException e) { } return data; } 客户端统计QPS和AVG逻辑#
获取当前时间戳,客户端在和服务端建立连接后,会每隔1s给服务端发送数据,发送的数据就是当前的时间戳,服务端获取到这个时间戳以后,会把这个时间戳再次返回给客户端,所以客户端会拿到发送时候的时间戳,然后客户端用当前时间减去收到的时间戳,就是这个数据包的处理时间,记录下这个时间,然后统计数据包发送的次数,根据这两个变量,可以求出QPS和AVG,其中:
QPS 等于 总的请求量 除以 持续到当前的时间
AVG 等于 总的响应时间除以请求总数
客户端源码参考:Client.java
服务端源码参考:Server.java
服务端在不做任何优化的情况下,关键代码如下... bootstrap.childHandler(new ChannelInitializer() { @Override protected void initChannel(SocketChannel ch) { ch.pipeline().addLast(new FixedLengthFrameDecoder(Long.BYTES)); ch.pipeline().addLast(/*businessGroup,*/ ServerBusinessHandler.INSTANCE); // ch.pipeline().addLast(ServerBusinessThreadPoolHandler.INSTANCE); } }); ... @ChannelHandler.Sharable public class ServerBusinessHandler extends SimpleChannelInboundHandler { public static final ChannelHandler INSTANCE = new ServerBusinessHandler(); @Override protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) { ByteBuf data = Unpooled.directBuffer(); data.writeBytes(msg); Object result = getResult(data); ctx.channel().writeAndFlush(result); } protected Object getResult(ByteBuf data) { int level = ThreadLocalRandom.current().nextInt(1, 1000); int time; if (level <= 900) { time = 1; } else if (level <= 950) { time = 10; } else if (level <= 990) { time = 100; } else { time = 1000; } try { Thread.sleep(time); } catch (InterruptedException e) { } return data; } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { // ignore } }
运行服务端和客户端,查看客户端日志..... qps: 1466, avg response time: 35.68182 qps: 832, avg response time: 214.28384 qps: 932, avg response time: 352.59363 qps: 965, avg response time: 384.59448 qps: 957, avg response time: 403.33804 qps: 958, avg response time: 424.5246 qps: 966, avg response time: 433.35272 qps: 980, avg response time: 484.2116 qps: 986, avg response time: 478.5395 ..... 优化方案一:使用自定义线程池处理耗时逻辑#
将服务端代码做如下调整bootstrap.childHandler(new ChannelInitializer() { @Override protected void initChannel(SocketChannel ch) { ch.pipeline().addLast(new FixedLengthFrameDecoder(Long.BYTES)); //ch.pipeline().addLast(/*businessGroup,*/ ServerBusinessHandler.INSTANCE); ch.pipeline().addLast(ServerBusinessThreadPoolHandler.INSTANCE); } });
其中ServerBusinessThreadPoolHandler中,使用了自定义的线程池来处理耗时的getResult方法。关键代码如下:private static ExecutorService threadPool = Executors.newFixedThreadPool(1000); @Override protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) { ByteBuf data = Unpooled.directBuffer(); data.writeBytes(msg); threadPool.submit(() -> { Object result = getResult(data); ctx.channel().writeAndFlush(result); }); }
再次运行服务端和客户端,可以查看客户端日志,QPS和AVG指标都有明显的改善.... qps: 1033, avg response time: 17.690498 qps: 1018, avg response time: 17.133448 qps: 1013, avg response time: 15.563113 qps: 1010, avg response time: 15.415672 qps: 1009, avg response time: 16.049961 qps: 1008, avg response time: 16.179882 qps: 1007, avg response time: 16.120466 qps: 1006, avg response time: 15.822202 qps: 1006, avg response time: 15.987518 ....
实际生产过程中,Executors.newFixedThreadPool(1000);中配置的数量需要通过压测来验证。优化方案二:使用Netty原生的线程池优化#
我们可以通过Netty提供的线程池来处理耗时的Handler,这样的话,无需调整Handler的逻辑(对原有Handler无代码侵入),关键代码:bootstrap.childHandler(new ChannelInitializer() { @Override protected void initChannel(SocketChannel ch) { ch.pipeline().addLast(new FixedLengthFrameDecoder(Long.BYTES)); // ch.pipeline().addLast(ServerBusinessHandler.INSTANCE); // 使用业务线程池方式 // ch.pipeline().addLast(ServerBusinessThreadPoolHandler.INSTANCE); // 使用Netty自带线程池方式 ch.pipeline().addLast(businessGroup,ServerBusinessHandler.INSTANCE); } });
其中businessGroup是Netty自带的线程池EventLoopGroup businessGroup = new NioEventLoopGroup(1000);
ServerBusinessHandler中的所有方法,都会在businessGroup中执行。
再次启动服务端和客户端,查看客户端日志..... qps: 1027, avg response time: 23.833092 qps: 1017, avg response time: 20.98855 qps: 1014, avg response time: 18.220013 qps: 1012, avg response time: 17.447332 qps: 1010, avg response time: 16.502508 qps: 1010, avg response time: 15.692251 qps: 1009, avg response time: 15.968423 qps: 1008, avg response time: 15.888149 .....更多优化建议#
参考Netty性能调优奇技淫巧还有其他的吗?
1.如果QPS过高,数据传输过快的情况下,调用writeAndFlush可以考虑拆分成多次write,然后单次flush,也就是批量flush操作
2.分配和释放内存尽量在reactor线程内部做,这样内存就都可以在reactor线程内部管理
3.尽量使用堆外内存,尽量减少内存的copy操作,使用CompositeByteBuf可以将多个ByteBuf组合到一起读写
4.外部线程连续调用eventLoop的异步调用方法的时候,可以考虑把这些操作封装成一个task,提交到eventLoop,这样就不用多次跨线程
5.尽量调用ChannelHandlerContext.writeXXX()方法而不是channel.writeXXX()方法,前者可以减少pipeline的遍历
6.如果一个ChannelHandler无数据共享,那么可以搞成单例模式,标注@Shareable,节省对象开销对象
7.如果要做网络代理类似的功能,尽量复用eventLoop,可以避免跨reactor线程
大手笔!三菱化学拟投1。1亿欧元,扩大聚酯薄膜生产能力近期,三菱化学在其官网上宣布,将投资1。1亿欧元用来扩大其子公司三菱聚酯薄膜有限公司(MFE)位于德国威斯巴登的聚酯薄膜生产能力。此次新增生产线的设计产能为年产27000吨聚酯薄膜
开学换机就要硬核宠粉海信阅读手机带你徜徉书香世界一转眼暑假就要结束了。随着开学季的到来,不少学生纷纷开始为自己准备学习用品,其中也包括智能手机这个日常使用频率最高的电子产品。然而,市面上的手机产品五花八门,作为学生到底应该怎么选
用车开到转速红区到底伤不伤车呢最近有小伙伴问,车子不小心开到转速红区,会不会伤车?下面我们一起来了解下。转速红区转速表上一般标有数字010之间的一段区域,单位是千转分钟,如果指针指向6,也就是说现在发动机转速是
车后的挡风玻璃为什么有很多横线关于汽车里的学问真是太多了,家里有车的朋友们,如果你们留心观察会发现一个问题,车辆后窗玻璃上会有一道道的横线,有的是红色,有的是白色,那么这个横线有什么作用呢?这些横线到底有什么作
为什么LED大灯没有普及有啥缺点LED车灯是主流,未来更多的车型将配备LED车灯,甚至激光灯组。现在没有普及的原因只有一个成本问题。很多入门车型配备卤素灯,中高配则配备了LED灯组。很多国产车则是全系配备了LED
星越S店内可试乘试驾售价13。57万元起星越S给你劲爽礼遇,8月31日前购车1焕新礼6000元置换补贴,2000元增购补贴,至高7000元置换补贴2金融礼至高金融贴息8000元3保养礼4。5年或12万公里免费保养4保值礼
万兴PDF,一款很实用的办公软件,工作上的好帮手因为工作上经常需要处理大量PDF文档,我刚开始是下载了些办公软件来处理它们,但是每次使用这些软件都不大方便,不是缺少某项功能,就是处理起来需要耗费大量的时间。无可奈何,我便询问起了
华为高端一体机最佳购买时机,华为MateStationX双十一来了现如今,随着5G时代的全面来袭,更多的智能产品走进了我们的生活。要说在智能领域中,最让国人倍感自豪的品牌,还要属华为。近年来,华为的每个举动都备受人们关注,研发的产品更是满满的创新
小米平板5明日再次开售在上周举行的2021雷军年度演讲上,小米MIX4系列正式发布,同时,还带来了小米平板系列新成员小米平板5。8月16日,小米平板5系列迎来首销,根据小米公布的战报来看,小米平板5系列
华为P50Pro屏幕全球第一超三星昨天,全球著名的权威评测机构DxOMark更新了华为P50Pro的屏幕测试,并且为其给出了93分的全球最高分,这是华为旗舰机的屏幕首次成为全球第一,超过了三星最强旗舰S21Ultr
华为公布最新进展,鸿蒙OS再次加快进度了?鸿蒙OS的系统自从被上线,就一直被大家所关注着的!尤其是广大花粉,他们都想知道自己的华为手机,什么时候才能真正升级为鸿蒙系统。而就目前来看,华为进一步加快了鸿蒙OS的升级进度,已经