1 背景
开放平台自上线以来还没有做过压测,刚好最近升级了 web 框架,想借此机会看看性能。
压测场景主要三个:
- 基准测试,旨在量化性能指标;
- 容量测试,逐步增加并发数,根据曲线拐点模型做容量规划;
- 稳定性测试,进行长时间压测(24小时、7天),观察内存泄漏、宕机;
2 准备
2.1 搭建压测环境
遵循生产一致原则,参考生产环境申请了最小set:
服务容器 4C8G
键值存储 Redis-3.2.3 @2C-16G-30Gi 3主集群
文档存储 MongoDB-3.6.13-wiredTiger 分片架构(1 primary、1 arbiter、3 secondary)@16C-40G-5Ti
关系存储 MySQL-5.7.25 @4C-16G-500Gi 1主1从2异地灾备架构
消息队列 RocketMQ-4.3.0 3主3从集群
协调服务 ZooKeeper-3.4.10 5节点集群
2.2 标定
常见的性能指标有并发数(虚拟用户数)、吞吐量(qps、tps)、响应时间/延迟(rt)、成功率。
期望在并发量 200 的情况下,吞吐量大于 500 TPS,响应时间少于 500 ms,成功率大于 95%。
这儿的成功率 95% 定低了,服务正常的情况下只有一个9的可用性,远达不到主管要求的4个9。
2.3 梳理请求链路
压测平台为北京、东莞两个机房分别提供了25个执行节点,由于我们的服务容器在东莞,执行节点可以通过内网DNS 走内网IP 访问。
接入方面,使用 Nginx 反向代理,对外暴露 Https,没有内网域名(Http),因为不能忽视 TLS 的性能损耗。
ServiceKeeper 提供流控、熔断、重试、降级、路由、鉴权功能,需要提前在 UI 停掉所有流控规则。
Restlight 提供自我保护功能,需要提前关闭新建连接数限制,以及 CPU load 保护。
Restlight 提供预热功能,可以设置一个延迟暴露时间,以等待本地缓存等资源初始化,压测需要关掉此功能。
开放平台 springMVC 版本依赖 Tomcat 实现的 servelet 容器,启动起来需要1秒,压测前要先请求一遍。
2.4 请求接口
2.4.1 用户授权事务
用户授权事务由3个接口组成:用户授权、生成 access-token、回收用户授权。生成 token 后设置了一个 3s 的思考时间,更加符合真实场景。
2.4.1.2 SpringMVC 实现
并发 200,吞吐量 293 TPS,平均响应时间 374.43 ms,成功率 99.641%。
查看错误统计,错误都是报重复生成异常,这是因为随机生成 ssoid 里有重复的,这个不可抗,因此实际成功率能有 99.99%。
2.4.1.1 Restlight 实现
并发 200,吞吐量 553 TPS,平均响应时间 228.7 ms,成功率 99.577%。
相较于 SpringMVC,该实现吞吐量提升了 88.73%,延迟降低了 38.92%。
Restlight 带来的性能提升还是相当可观的,具体原因我将另著一文阐述。
2.4.2 查询用户授权
并发 200,吞吐量 2894 TPS,平均响应时间 38.07 ms,成功率 100%。
这个吞吐量是不正常的,本人经验 2000 的 TPS 要引入 Redis,4000 以上的 TPS 要全程使用本地缓存。
该接口要查询2次以上的数据库,有可能是 Restlight 在底层对相同的入参的请求做了缓存,否则不可能将近 3000 的 TPS。
2.4.3 查询数据接口
并发 200,吞吐量 12658 TPS,平均响应时间 8.58 ms,成功率 100%。
该接口中间需要走一次网络IO,访问 OPPO 账号中心。
这么高的 TPS 不得不怀疑 PTMS 的 HttpClient 做了本地缓存,毕竟 GET 是幂等的,按照 HTTP 协议是可以放心缓存的。
3 分析
3.1 验收压测环境
在申请压测环境的时候,参考了生产环境的主机规格,我们申请的容器 4C 实际上对应的是逻辑CPU。
$ cat /proc/cpuinfo| grep "physical id"| sort| uniq| wc -l
1
$ cat /proc/cpuinfo| grep "cpu cores"| uniq
cpu cores : 16
$ cat /proc/cpuinfo| grep "processor"| wc -l
4
$ cat /proc/cpuinfo | grep 'model name' |uniq
model name : Intel(R) Xeon(R) Gold 6130 CPU @ 2.10GHz
3.2 容量规划
根据曲线拐点模型,随着并发数上升,系统压力会经过3个阶段: