收钱吧生产环境全链路压测的实践
1. 背景
随着公司交易体量的不断增长,以及围绕“服务千万商家,全能生意帮手”的理念不断拼装的业务版图,曾经在某一段时间内发生了一些故障,给用户和商户带来非常不好的体验,也给公司带来较大损失。
-
线下我们明明做了各种验证测试,功能、性能等,怎么一上线还是各种问题? -
老板或者运营同学说,我们马上要做活动,线上能支撑的住吧? -
IT环境成本又增长了,但是业务又没有多大增长,能降低线上IT成本吗?
上面类似的问题,相信很多同学都碰到过,解决办法业界也有比较成熟的方案,就是全链路压测。
2. 解决方案
2.1 传统线上压测
在全链路压测之前,我们线上环境压测,往往采用如下方式进行:
-
线上环境预先准备好测试数据,模拟请求针对单个服务或者集群压测 -
nginx镜像一份流量来压测
上述的压测方式有以下问题
-
费时费力,前期需要构建大量测试数据 -
产生脏数据,污染线上数据库等 -
压测模型需人为构建,导致压测结果不精准 -
仅覆盖关键核心服务,压测覆盖面窄 -
难以覆盖链路上的基础设施,比如slb、nginx、网络、数据库等
2.2 现在方案
基于上述问题以及收钱吧具体业务需求,我们设计开发了全链路压测系统Havok。作为公司层级的全链路压测平台,主要设计目标就是为公司业务提供真实、安全的仿真流量输出,同时给业务团队提供准确的容量评估数据,所以我们主要提供如下能力:
真实重现用户行为,持续输出真实压测流量
真实复现线上用户行为,按时间轮回放真实流量,同时不污染线上数据,用户无感
提供按速率和倍数来放大流量
在上一步的基础能力上,可以按照预设的速率或者倍数来放大流量,进而探测线上容量
具备“开箱及测”的能力
不需要前期花费过多精力构造测试数据等准备工作,做到想什么时候压测就什么时候开始;同时不污染生产数据
支持多种压测类型
支持普遍的http接口压测,支持收钱吧内部的rpc请求压测以及移动设备的特殊协议压测
提供压测过程中实时的监控和过载保护能力
提供实时监控数据收集查看的能力,同时根据预定规则提供自动停止压测来保护线上环境的能力
3. 系统架构设计
我们采取的方式是回放生产服务日志来实现压测,包括读和写操作,同时根据日志的时间戳来控制阻塞/放行请求,来达到高仿真压测的目的,整体架构如下:
-
Havok-dispatcher【调度中心】
:负责线上服务日志数据的下载、排序(有规则约束)、时间控制、请求分发、压测引擎监控数据的收集和上送等功能 -
Havok-replayer【压测引擎】
:负责根据既定规则回放dispatcher
订阅的压测请求、支持回放请求的增益调整、支持压测数据的规则调整等功能 -
Havok-monitor【监控平台】
:收集并展示压测引擎的数据、线上服务的监控数据、中间件等数据 -
Havok-mock【mock服务】
:提供mock
服务功能 -
Havok-canal【数据构造】
:提供影子数据的实时增量清洗
4. 主要模块功能介绍
4.1 调度中心核心功能
调度中心核心功能是日志抽取以及请求下发,支持多个数据源、支持维度筛选、保序日志分发、增益能力、监控数据查看上送、压测引擎的管理调度。整体设计如下图:
4.1.1 为什么采取回放方式
假设有这么一个外卖下单接口: POST /api/order
,需要传入商户ID、菜品ID等参数,如果计划用100并发线程去压这个接口,你肯定会考虑以下几个问题:
-
是使用同一商户、同一菜品去压测? -
是使用同一商户、不同菜品去压测? -
是使用100个商户、不同菜品去压测? -
是使用20个商户、每个商户相同菜品去压测?
场景设置外,还有其他诸如:
-
缓存如何控制?是否需要避免命中缓存?是否考虑命中缓存概率? -
查询请求,是否要考虑存量数据的查询效率影响?是否考虑数据库分库分表? -
.....
这么多场景构造,很麻烦...
日志回放方式就相对较好的解决上面的困扰,因为压测请求场景的构造,日志里已经告诉我了,要做的就是塞到压测请求里回放就行了,我不需要考虑如何设计。同时利用好现有的数据库表、缓存等设施,我也就不需要考虑数据多样性等问题
4.1.2 如何做到高仿真
除了如实的回放请求内容外,我们还得控制好回放的节奏,就像一首歌,同样的磁带,播放的速度不一样那出来的节奏肯定不一样。所以我们还严格按照日志的时间戳来控制好回放的节奏,做到回放出的不仅歌词一样,同时节奏也一样。timewheel
模块就承担了这样的职责,支持快放、慢放。
4.1.3 增益能力
为了满足业务容量探测设计,提供了请求的增益(倍率)能力,能将一个请求复制出多份,从而实现对业务增长的模拟。另外,即便一比一等量回放,我们依然要面对回放型压测中不得不解决的一个问题:幂等性。
f(x) = f(f(x))
举个例子:我们的创建商户接口 POST: /api/createMch
,要求传入的商户名称是唯一的(业务要求),这个时候日志里包含了一个创建商户的动作,创建了一家小吃店:XX小吃。如果你从日志中提取后,重放这个请求,必然创建失败:数据库中已经存在该商户。
因此不但需要实现请求增益能力还需要解决接口幂等性的问题。目前我们两个思路:
-
Havok
接口层面支持自定义关键字偏移修改,避免冲突 -
被压服务层面做好幂等处理
4.2 压测引擎核心功能
压测引擎主要功能有:
支持分布式容器化部署
容器化部署,可以快速扩展压测引擎能力
异步发送/接收消息
采用异步请求方式处理请求和响应,提高并发能力,Havok
基于goroutine
来实现,如下好处:
-
goroutine的切换没有内核开销 -
内存占用小,线程栈空间通常是2M,goroutine栈空间通常是2K -
G-M-P调度模型
接口请求和响应字段的过滤调整
针对敏感数据统一处理;根据预定规则偏移数据以方便压测;针对响应进行定制化assert
接口层级各个维度的统计
统计接口错误率、吞吐量、P95等指标并上报给调度中心
响应调度中心事件下发处理
处理调度中心下发的开始压测、停止压测、熔断等指令
4.3 数据构造
传统的压测很大一部分工作就是前期的数据构造,面临如下等问题:
-
数据样本类别分布不均衡,多样性不足 -
数据量偏少 -
构建数据耗时过长
所以我们基于阿里的canal[1]增量同步服务定制开发了符合我们压测要求的功能,比如偏移数据等功能。达到想什么时候压测就什么开始,相比以往大大提高了执行周期效率。
-
敏感数据,诸如电话号码、身份证号等统一脱敏偏移处理 -
商户号、门店号、终端号等信息按既定规则偏移处理,不影响实际线上商户使用
4.4 Mock第三方服务接口
针对第三方服务,我们采取的是mock处理,mock支持延时抖动等特性,同时通过事后的正态分布分析调整mock相关配置,尽量匹配生产相关处理的生命周期模拟;
-
自研通用mock服务 DeepMock [2]:压测前,需要注入mock规则以及响应规则
4.5 压测监控
线上压测肯定会线上服务产生一定影响,所以我们需要做到对相关链路秒级的监控,以及针对异常快速做出熔断等反应,架构如下
4.5.1 施压端监控
压测引擎会每秒汇总好接口层级的监控数据上送给调度中心再处理,指标包括接口错误率、吞吐量、top90、top95、avg响应时间等
4.5.2 服务端监控
由于公司服务设施基本部署在云上,所以服务端监控基本依赖现有云端监控设施,包括中间件监控
4.6 压测隔离
线上压测要保证压测行为真实、安全且可控,不会影响正常用户的使用,并且不会对线上环境造成任何数据的污染。要做到这一点,首先需要解决的是压测流量的识别与透传问题。有了压测标识后,各服务与中间件就可以依据标识来进行压测服务是否隔离、流量是否透传以及影子表等方案的实施。
4.6.1 压测标识透传
压测标识,目前我们采取的key:value
方式染色压测流量,同时我们相关链路的服务以及基础RPC框架等中间件都已配合改造以识别。
改造原理是将压测标识的 kv 值存入 Context
中,然后发往下游的请求中都带上该 Context
,下游服务可以根据 Context
中的压测标识完成对压测流量的识别和处理。在实际业务中,代码改造也非常简单,只需要透传 Context
即可。
4.7 数据隔离
线上压测相对比较复杂的就是如何确保压测产生的写数据不污染线上数据,业界常见做法比如影子表、影子库,以及数据偏移等。
我们针对不同存储,采取不同策略:
-
MySQL、MongoDB:采用影子表方式,应用层面根据压测标识来区分压测流量读影子表、写影子表;影子表数据的偏移规则支持[stress_tag]前缀、随机字符串、uuid、字符反转等等替换方式
-
Redis:key偏移后使用,压测完失效或者删除即可
-
Kafka或者MQ:两种策略,一种业务层面直接丢弃不下发,然后后续单独压测;或者透传压测标识,consumer根据自身业务情况改造处理
-
其他存储:比如ES,单独压测集群,压测时启用
4.8 熔断保护机制
4.8.1 施压端熔断
Havok
会实时分析监控指标数据,根据业务配置阈值,实时给压测引擎下发降低QPS或者停止压测事件,防止压垮线上系统
4.8.2 服务端熔断
线上业务自身具备熔断能力,基于相应中间件实现,根据阈值,比如错误率自动熔断
5. 压测实施
目前公司部分核心业务已经接入,包括门店码支付、被扫支付、小程序支付等等,后续业务也在陆续推进接入中,随着业务线压测的推进,我们从中也学到了大量的业务知识,架构知识,接收到不少反馈,持续推动我们不断演进和优化Havok
。目前已经开源Havok[3],欢迎志同道合者提出宝贵意见或者PR。
6. 总结与展望
从项目立项到成功投产压测,得到了产研同学的大力支持和帮助,尤其是各个业务线配合改造,真心感谢,正是在大家的配合下,才能够初具规模。展望未来我们还有较长路需要走,期望能够帮助大家提高研发效率,及时发现性能问题。
6.1 易用性提高
由于多种因素影响,目前我们在用户易用性上还有待提高,期望后续在可视化操作上投入更多精力,尽量降低研发同学使用难度,傻瓜式操作。
6.2 压测与SLA建设
如何精准评估业务线的容量规划?按照目前的压测数据,动态评估还能支撑多久的业务规模增长?线上机器资源是否可以优化,进一步降低成本?如何配合公司层级的混沌测试?这些问题都需要我们推进和解决中。
7. 关于作者
费翔,来自质量工程部
参考资料
阿里巴巴 MySQL binlog 增量订阅&消费组件: https://github.com/alibaba/canal
[2]deepmock: https://github.com/wosai/deepmock
[3]havok: https://github.com/wosai/havok