压测目的 本次压测意在寻找性能拐点,评估最大处理能力,并不断摸索操作系统与应用的调优参数
压测方案 压测场景 模拟单灯控制器,建立TCP链接,发送登录报文。登录成功后,不间断的发送属性上报消息。
创建虚拟设备 编写简易的JUnit脚本,以便于批量创建删除设备
录入10万个设备,设备ID为: 000000000001~000000100000
编写设备模拟器 由于单灯控制器通过TCP私有协议与平台进行通信,且上报消息的内容不固定,所以很难使用JMeter等常规压测工具进行测试。
需要自行编写单灯控制器模拟器,做为压测客户端,使用VertX异步框架开发以提高并发性能。
服务器指标监控 部署Prometheus
+Grafana
,进行图形化监控,方便查询历史指标并找出瓶颈点
消息样例
登录(42字节): 68230068000000002000002226130007500100020802034C4F43373930302D4331454C36313000A35116
属性上报(58字节): 68330068010000001300000e0000002001011003112700010fce590000003b400000dd242a426400000000000000000000000014000000f01216
压测环境搭建 服务端配置
1台普通台式机,配置为i7-7700 4核8线程/16G/256G/千兆LAN
最小化部署,应用及中间件混合部署在同一台服务器
服务端组件清单
类别
名称
版本号
应用
bifrost-ui
1.0.0
应用
bifrost-app
1.0.0
中间件
redis
5.0.4
中间件
elasticsearch
6.8.11
中间件
kibana
6.8.11
中间件
postgres
11-alpine
JDK
openjdk
1.8.0_382
监控
grafana
10.3.3
监控
prometheus
2.50.1
监控
nodeExporter
1.7.0
服务端地址信息
压测客户端
4台Ubuntu虚拟机,配置为:8C/16G/500G/千兆LAN(实际只用到2台)
Ulimit与内核参数的优化,与服务端保持一致
部署自行编写的单灯控制器模拟器
启动参数(以1万并发,120秒为例):
1 java -jar catonelight-simulator-1.0.0-SNAPSHOT.jar --host=10.90.22.200 --port=1886 --start=1 --parallels=10000 --duration=120
压测详细记录 请参考11~12轮的结果即可,点击跳转至语雀在线表格:https://tc-aiot.yuque.com/org-wiki-tc-aiot-ms6e4o/tabv3n/zug68i5yymut8vlg#CyG5
结果分析
8核16G的单机配置下,极限吞吐量为95502 TPS,极限并发数约为80000个设备
随着并发数的不断提升,TPS保待不变,响应时间成比例的增加。系统并未出现明显拐点。
内存占用量平稳,6-8G,基本无变化,上下文切换保持在60K左右
系统负载稳定保持在15左右(处理器为8核心),以上都是Reactor带来的收益
日志IO对性能的影响比预想中大很多,需要最先调优,同时需要兼顾实用性,不能完全关闭。优化后的性能提升约15倍。
Docker方式和原生JDK方式,在参数相同的情况下,TPS会有较大差异,各有胜负,原因不明
关闭TCP粘拆包处理,可获得约30%的性能提升
关闭Reactor的DebugAgent后,可获得约40%的性能提升
参数调优 操作系统Ulimit 1 2 3 4 5 6 7 cat <<EOF >> /etc/security/limits.conf root soft nofile 1048576 root hard nofile 1048576 root soft stack 1048576 EOF sysctl --system
操作系统内核参数(修改前) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 cat <<EOF >> /etc/sysctl.conf net.core.rmem_max=16777216 net.core.wmem_max=16777216 net.core.netdev_max_backlog = 30000 net.core.somaxconn = 262144 net.ipv4.neigh.default.gc_stale_time=120 net.ipv4.conf.all.rp_filter=0 net.ipv4.conf.default.rp_filter=0 net.ipv4.conf.default.arp_announce = 2 net.ipv4.conf.lo.arp_announce=2 net.ipv4.conf.all.arp_announce=2 net.ipv4.ip_local_port_range = 1024 65535 net.ipv4.tcp_rmem=4096 87380 16777216 net.ipv4.tcp_wmem=4096 65535 16777216 net.ipv4.tcp_fin_timeout = 10 net.ipv4.tcp_tw_recycle = 1 net.ipv4.tcp_timestamps = 0 net.ipv4.tcp_window_scaling = 0 net.ipv4.tcp_sack = 0 net.ipv4.tcp_syncookies = 0 net.ipv4.tcp_max_orphans = 262144 net.ipv4.tcp_max_syn_backlog = 262144 net.ipv4.tcp_max_tw_buckets = 5000 net.ipv4.tcp_synack_retries = 2 net.ipv4.tcp_syn_retries = 2 net.ipv4.tcp_no_metrics_save=1 net.ipv6.conf.all.disable_ipv6 = 1 net.ipv6.conf.default.disable_ipv6 = 1 net.ipv6.conf.lo.disable_ipv6 = 1 vm.swappiness = 0 kernel.sysrq=1 fs.file-max=1000000 #fs.file-nr=1000000 vm.overcommit_memory = 1 EOF sysctl -p
操作系统内核参数(修改后) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 net.ipv4.tcp_max_tw_buckets=50000 net.ipv4.tcp_max_syn_backlog=262144 net.core.somaxconn=262144 net.ipv4.tcp_tw_reuse=1 net.ipv4.tcp_tw_recycle=1 net.ipv4.tcp_rmem=4096 87380 16777216 net.ipv4.tcp_wmem=4096 65535 16777216 net.ipv4.tcp_mem=786432 2097152 3145728 net.ipv4.ip_local_port_range=1024 65535 vm.swappiness=0 kernel.sysrq=1 fs.file-max=1048576 vm.overcommit_memory=1
TCP粘拆包脚本 1 2 3 4 5 6 7 8 parser.fixed(4 ) .handler(function (buffer, parser ) { var len = buffer.getShortLE(1 ); parser.fixed(len + 3 ).result(buffer); }) .handler(function (buffer, parser ) { parser.result(buffer).complete(); });
默认情况下,可以不启用粘拆包,以获得30%以上的性能提升
日志优化
开启Console日志+文件日志+设备ES日志
并逐行调整日志级别,大部分置为INFO级,兼顾实用
关闭Logback的ES日志(怀疑这部分程序有问题会产生阻塞,关闭后TPS提升20倍)
应用优化
关闭Reactor调试模式 spring.reactor.debug-agent.enabled=false,可获得约40%的性能提升
关闭平台链路跟踪 trace.enabled=false
后续如有必要时,将单个TCP连接的接收队列长度由默认256调高到65535,以应对单一设备连续上报的场景。需要修改VertX代码。
JVM优化 1 2 3 4 5 6 java -Duser.language=zh -XX:+UseG1GC -server \ -XX:+UnlockExperimentalVMOptions \ -XX:+UseCGroupMemoryLimitForHeap \ -XX:-OmitStackTraceInFastThrow \ -Djava.security.egd=file:/dev/./urandom \ -jar bifrost-standalone.jar