达达集团是 2014年在上海成立的本地即时配送平台, 过去7年中稳步前进; 2020年双11大促时, 日配送完成单量突破 1000万; 单量的增长需要稳定性的保障, 全链路压测在达达稳定性保障中扮演着重要角色,本文将阐述达达全链路压测的核心设计与落地。
达达原压测方案是: 搭建一套跟线上 1:1 的压测环境, 在这套环境中进行压测, 此方案技术难度低, 实现简单, 但弊端也明显: 人力及机器成本随着生产环境规模的变大而变大; 于是我们调研了业界主流的 【流量打标】方案, 该方案原理是: 在请求的流量(HTTP,RPC,MQ)上打标, 所打的标示随着请求在各个服务之间流转, 从而使得压测流量与线上流量隔离; 在数据隔离这块:
-
DB 层: 使用 影子库/影子表 隔离数据。
-
Cache 层: 使用 影子缓存 隔离数据;。
-
MQ 层: 使用 影子队列 隔离数据。
但这个方案适用于中间件统一的场景;
而达达内部使用了各种类型的中间件, 比如 ORM 就有 Mybatis, Hiberante, JPA, 版本也不一致; 并且存在大量异构程序, 有 Java 的, 有Python 的; 若要实现【流量打标】方案, 势必会有大量业务改造, 因此我们放弃此方案。
在分析达达自身架构特点后, 我们在 2019年一季度研发了基于【机器打标】的压测方案 (数据使用 影子DB&Redis&MQ 隔离)。
-
机器抽象化
: 所有 DB&Redis&ES 机器抽象成一个一个节点; 节点信息如下图。
-
机器信息注册到注册中心
: 所有节点信息注册到注册中心。
-
服务接入链路治理SDK
: 所有服务接入 "链路治理SDK", "链路治理SDK" 具有根据链路路由请求的能力。
-
cloud_name
: 资源名称, 全局唯一, 一个资源可包含多个节点, 比如 mysql_cluster001 就可以包含一个主节点, 多个从节点。
-
node_name
: 节点
名称, 全局唯一, 通常使用 host+port。
-
host
: host 信息。
-
port
: 端口信息。
-
role
: 角色, w: 写节点, r: 读节点。
-
dada_type
: 资源类别, 有 mysql, redis, es 等。
-
data_link
: 链路类别, 有 benchmark(压测), base(生产)。
方案中最重要的是 "链路治理SDK", 它的职责是: 根据链路类别路由请求流量; 如下图所示:
-
注册服务节点信息。
-
根据本机的链路类别, 获取对应链路的存储节点信息。
-
根据存储节点信息, 建立起 DB,Redis, MQ 连接。
最终线上环境, 在机器维度形成两条链路, 处理生产流量的生产链路和处理压测流量的压测链路。
下图是对【流量打标】【机器打标】的比较, 两种方案都有优缺点, 达达从安全及系统改造成本出发, 最终选择了【机器打标】方案。
原压测方案使用 jmeter 进行压测, jmeter 属于老牌压测工具, 稳定性高且支持分布式; 但在压测场景复杂时,使用不够灵活; 达达的压测多数为复杂场景压测, 所以我们放弃原方案, 转而基于 jmeter 内核开发自己的压测平台; 以下是压测平台的整体架构:
-
前端服务
: 提供 压测任务填写, 压测任务启动/停止, 压测结果展示等功能。
-
压测任务解析器
: 主要负责压测任务的解析, 存储。
-
压测引擎
: 负责将压测任务调度到执行器上执行 (定时执行 & 立即执行)。
-
压测结果处理器
: 负责对压测接口返回值解析, 统计, 异常处理, 并生成报表。
新压测平台的优势在于: 具有可视化的操作界面, 压测结果实时动态产出; 研发人员在压测平台配置 "发压性能参数", "造数据脚本" 就能直接执行。
压测引擎在施压时, 对应压测结果 (TPS&响应&错误率) 会实时展示到前端界面。
整个全链路压测的落地分成: 压测前, 压测中, 压测后:
其中
压测前
有三个关键点: 压测链路梳理, 优化预案设定, 精细化压测模型。
链路的梳理非常重要, 它决定着压测链路需要部署哪些服务, 压测时哪些服务需要被关注。
以前达达通过人肉的方式梳理链路, 但是这种方案效率低, 不准确, 工作量大, 且当生产环境链路变更时, 我们不能即时感知到; 后面引入 APM(PinPoint), APM 有梳理链路的功能。
但此方案还是没解决: "实时感知链路变更" 的问题;
为此我们在开发环境拉了条链路, 定时发请求以检测是否链路通畅; 若链路依赖有变化, 我们就能立刻知道。
俗话说:
不打无准备之战
, 压测就是为了提前发现高负载时系统可能出现的性能隐患, 那如何解决性能隐患呢? 通常在压测之前我们会准备一些性能优化预案, 常用预案如下:
因过去几年的业务的发展, 导致生产环境数据库单表单库的数据量一直在增长, 达达物流系统多次碰到
Mysql 主从延迟
; 目前最常用的优化方案是 "Mysql BinLog 调优", 此方案主要调优以下两个参数:
-
binlog_group_commit_sync_delay
: 表示事务提交后, 等待多少时间, binlog 再同步到磁盘, 默认0, 表示不等待(单位微秒)。
-
binlog_group_commit_sync_no_delay_count
: 表示等待多少事务提交后, binlog 再同步到磁盘。
但此调优方案也有弊端: 参数调优后, 接口响应会有一定提升(如下图); 所以调优时需考虑业务能否容忍接口响应的上升。
随着业务的发展, 压测模型也在不断演进迭代中; 从一开始 "使用虚拟骑士, 单一按照接口TPS目标值压测" 到现在 "模拟生产活跃骑士, 引入时间&空间因素 构建压测模型",
模型越发精准。模型分成两类:
-
数据模型
: 骑士数据, 商户数据, 订单数据。
-
流量模型
: 订单下发, 配送履约。
把生产上活跃骑士&商户&订单导入影子库, 对这批数据进行清洗, 去除敏感信息(手机号, 地址信息等), 用这些数据进行压测; 此方案简单且能最真实的还原生产环境。
业界常用 【流量回放】方案, 将大促时生产环境的流量存储下来, 然后在压测环境进行回放。
此方案适用于读接口的压测, 但是达达的业务场景复杂, 主流程接口多写少读; 所以直接使用此方案肯定不行。
鉴于不能直接使用【流量回放】, 达达选择了【人工构造流量】。影响构造流量真实性主要有两大因素: 时间, 空间。
在时间上, 达达一天有三个高峰期: 早&午&晚高峰, 每个高峰期 各个接口处于不同状态:
-
早高峰
: 发单, 接单, 订单详情 的请求量处于高峰, 其他接口请求量一般。
-
午&晚高峰
: 订单详情, 取货, 完成 的请求量处于高峰, 其他接口请求量一般。
所以压测时, 达达根据业务接口三个高峰期的特点, 设计两个压测场景(午高峰与晚高峰接口状态类似, 合并成一个压测场景), 进行压测。
在空间上, 订单与骑士不均匀的分布在各地, 有些区域人多单少, 有些区域人少单多, 而对系统影响最大的是人多单多的热点区域。
达达有个
查看周围X公里订单
的接口, 这个接口的性能跟 热点区域的个数, 每个热点区域内的单量, 每个热点区域内的运力 关系比较大; 为了搞清这几点, 我们分析大促时生产的流量, 根据 geohash 把全国划分成一个个正方形区域, 统计每个正方形区域内的订单及运力; 最后再在压测环境还原并放大。