秒杀系统性能压测)
本文为「电商秒杀架构」系列第三篇基于前文设计的秒杀系统对服务器配置、第三方组件Redis、RocketMQ以及核心业务接口进行系统化的性能压测并给出压测初步结论。一、秒杀系统快速回顾整体秒杀流程架构如下秒杀活动的关键流程可以概括为 7 步运营人员在秒杀系统的运营后台根据指定商品创建秒杀活动指定活动的开始时间、结束时间、活动库存等。活动开始之前由秒杀系统运营后台开启秒杀会同时往商城系统的 Redis Cluster 集群写入首页秒杀活动信息以及往秒杀系统的 Redis 主从集群写入秒杀商品库存等信息。用户进入到秒杀商详页准备秒杀。商详页可以看到「立即抢购」的按钮这里我们可以通过增加一些逻辑判断来限制按钮是否可以点击比如是否还有活动库存、是否设置了预约等。如果都没限制用户可以点击抢购按钮进入到订单确认服务展示秒杀结算页。在结算页用户可更改购买数量切换地址、支付方式等。确认无误后用户提交订单在这里后端订单服务通过 MQ 异步完成数据库中库存的扣减和订单的生成。订单完成后根据用户选择的支付方式跳转到对应的页面比如在线支付就跳转到收银台货到付款的话就跳到下单成功提示页。为了应对秒杀的巨大瞬时流量、热点数据问题等等我们还实现了很多的举措比如秒杀系统的隔离、商详页的静态化、流量管控等等。我们对秒杀系统的压测主要集中在秒杀商详页和秒杀下单这两个一头一尾两个步骤上。二、服务器基础配置首先对当前压测环境的服务器配置进行统计。当前整体服务器配置如下相关服务器物理 CPU逻辑 CPUCPU 主频内存秒杀下单服务与从 Redis142000 HZ4G秒杀订单服务142000 HZ4GNginx 服务器142000 HZ4G主 Redis142000 HZ4G三、基准测试接下来对主要的第三方组件 Redis 和 RocketMQ 做一下基准测试。3.1 主 Redis 基准测试当前秒杀系统采用一主一从的方式搭建 Redis。主节点主要负责同步秒杀库存数据以及实时扣减库存。我们先来测试一下主节点读写缓存的基准性能。在对 Redis 进行压测前需要注意一下 Redis 的几个对压测性能有比较大影响的配置redis.conf中的重要配置项。在主节点上我们使用 Redis 提供的redis-benchmark脚本进行基准测试预设 100 个并发线程进行 20W 次 get 和 set 操作测试的字节大小预设为 8 个字节。主要压测数据如下指标数值msavg0.718min0.208p500.743p950.935p991.183max2.1593.2 从 Redis 基准测试从节点主要负责同步秒杀库存数据其作用主要是供 OpenResty 读取库存用而 OpenResty 我们配置的是开启 7 个工作进程加上主进程一共是 8 个进程。所以我们在压测时将并发数改为 8。从经验判断这个 Redis 服务的读写性能是相当低的这跟当前电商项目采用的还是几年前的旧机器有关这有可能对后续整体性能产生比较大的影响。为了让大家能够形成比较直观的对比我们另外准备了一台阿里云上的云服务器基础配置为 1 CPU、2 核心、4 处理线程、主频 2500 HZ、内存 32G在上面进行同样的 Redis 压测结果如下基准测试还只是测试 Redis 空转的读写性能考虑到项目运行过程中数据变化会更大网络通信方面也会有性能损耗实际项目中 Redis 的并发读写性能还会更低。那么 Redis 的读写性能会对业务具体产生什么样的影响呢我们接下来对电商项目的商品详情页进行进一步压测。3.3 Nginx 静态网页基准测试我们先来验证 Redis 的读性能对秒杀的影响。以秒杀的商品详情页为例在秒杀系统中商品详情页处理分为两个步骤首先在秒杀开始时通过 Freemark 生成商品对应的静态详情页放到 OpenResty 当中然后秒杀过程中客户每次访问详情页都会在 OpenResty 中直接通过 Lua 脚本访问 Redis实时获取商品库存。我们先来测试 Redis 性能对于商品详情页性能的影响尝试访问一个秒杀商品的详情页http://192.168.65.28/seckill_3_29.html使用ab模拟 200 个线程发送 100000 个请求。测试结果表明这种情况下单台秒杀 Nginx 能提供访问静态 Html 网页的 QPS 在11K左右。四、秒杀业务测试接下来我们开始针对秒杀业务进行业务测试。4.1 秒杀详情页测试秒杀业务入口为商品详情页。详情页需要在静态页面的基础上再通过 Lua 脚本访问 Redis 获取实时库存。我们同样使用ab来对商品详情页进行测试测试地址为http://192.168.65.28/product?flashPromotionId3promotionProductId29memberId462245。压测命令与关键结果节选核心指标ab -c 200 -n 100000 http://192.168.65.28/product?flashPromotionId3promotionProductId29memberId462245 Server Software: openresty/1.21.4.1 Document Path: /product?flashPromotionId3promotionProductId29memberId462245 Document Length: 9934 bytes Concurrency Level: 200 Time taken for tests: 8.659 seconds Complete requests: 100000 Failed requests: 0 Write errors: 0 Requests per second: 11548.80 [#/sec] (mean) Time per request: 17.318 [ms] (mean) Transfer rate: 113897.75 [Kbytes/sec] received Percentage of the requests served within a certain time (ms) 50% 18 66% 19 75% 19商品详情页的 QPS 基本和静态网页保持在同一个水平甚至略高。这其中是因为 Nginx 对静态网页提供了缓存能力。4.2 商品库存数据获取接下来我们再针对商品详情页中最核心的从 Redis 读取库存的接口进行压测测试地址http://192.168.65.28/cache/stock?productId29。可以看到获取商品库存数据的 QPS 也保持在11K左右。所以综合上面的测试可以看到在当前一台 Nginx 服务器的情况下当前秒杀系统对商品详情页可以支持11K /s 左右的 QPS。4.3 秒杀下单接口测试接下来测试最为重要的秒杀下单接口。该接口整体的业务逻辑分为两个部分首先是从缓存中进行库存扣减然后异步发送消息到 MQ完成下单操作。因此在压测时我们也针对两个部分分别进行测试。1MQ 生产者基准测试由于在秒杀业务中我们只需要将下单消息发到 RocketMQ 即可所以我们这里也只需要测试生产者发送消息的基准性能。./producer.sh -n rocketmq1:9876 Send TPS: 22129 Max RT(ms): 719 Average RT(ms): 1.214 Send TPS: 11927 Max RT(ms): 2979 Average RT(ms): 2.738 Send TPS: 14069 Max RT(ms): 2979 Average RT(ms): 1.472 Send TPS: 18185 Max RT(ms): 2989 Average RT(ms): 1.542默认参数下我们这个 RocketMQ 集群的消息发送 TPS 大大低于理想的情况并且测试中看到失败的消息非常多。那么在业务中 RocketMQ 的情况怎么样呢2MQ 业务压测我们将往 RocketMQ 发送下单消息的功能单独抽取出来并在秒杀服务中提供了单独的测试接口进行性能测试接口中直接往 RocketMQ 中发送消息。压测结果如下这个压测的结果离理想情况差距非常大。这应该是受限于当前服务器的配置情况最主要是在部署时由于服务器内存不够因此我们对每个 RocketMQ 的运行内存进行了限制。鉴于这种情况我们在实际的下单接口中引入了异步线程池来提升往 MQ 发消息的性能。3秒杀下单接口压测接下来再整合业务代码对秒杀下单接口进行压测。压测之前先测试一下接口功能是否正常返回code:200表示正常。然后使用ab进行压测可以看到在各项服务基准测试都不太理想的情况下秒杀下单接口依然能够保持4000 左右的 TPS。五、压测初步结论可以看到在各项服务基准测试都不太理想的情况下秒杀下单接口依然能够保持4000 左右的 TPS。这个 TPS 承担互联网一般的秒杀活动还是基本可以的。说明本次压测环境存在明显短板——Redis 读写性能偏低旧机器、RocketMQ 因内存受限而限制了运行内存因此整体数据更偏向「下限验证」。在生产环境中通过更高规格的机器、合理的 Redis 集群与 MQ 调优整体吞吐还会有明显提升空间。