写点什么

稳定性全系列(二):如何做线上全链路压测

  • 2020-03-25
  • 本文字数:3164 字

    阅读完需:约 10 分钟

稳定性全系列(二):如何做线上全链路压测

1. 背景介绍

如今,在微服务架构盛行的互联网时代,微服务架构下模块(本文指可独立部署的服务)之间的关系错综复杂(哪怕是避免模块之间的直接循环依赖都很变得困难),评估一整套业务系统(集群)的容量已经不像评估单机系统那样容易,而系统的容量评估,是稳定性建设的核心内容之一,是我们绕不开的主题。


有了系统容量评估,配合今年的业务目标,我们才知道应该申请多少预算、什么时候需要扩容、系统瓶颈在哪、哪些服务(模块)需要扩容。评估系统容量或者准确的说 评估线上系统的容量现阶段最优效也是最准确的方式就是进行线上全链路压测

2. 准备工作

你要问实现线上全链路压测难不难?当然难(现阶段稳定性工作哪一项不难?),但依然有迹可循。而且和当前技术体系的系统化建设程度以及各团队之间协作有关系。想实现线上全链路压测,我们需要做如下三个方面的准备工作(为了描述简单,本文的“全压”指的是线上全链路压测):


  • 确定需要哪些团队参与

  • 确定全压技术方案

  • 设定全压目标和计划

3. 拆分详情

确定需要哪些团队参与

全压绝对是一项耗时耗力的工程,特别是刚开始的时候。首当其冲的当然是得到老大的支持,一般需要参与进来的至少有 研发测试运维 三个团队。研发团队主要负责技术方案的设定和实施(当然如果有架构组或中间件团队,技术方案的设定可以交给他们),测试团队负责验证全压方案和数据的正确性以及真正的施压,而运维团队需要关注压测对线上集群的影响以及一些辅助工作(例如提前调整网关的限流阈值)。

确定全压技术方案

这一步应该是难度最大的,不同技术体系具体实施方案当然不一样,但可以相互参考,就拿我的业务部门举例,我们服务端是 Java 栈,整个业务流量符合如下链路:



上图最左边的 App 指的是用户手机中装的 App,从后面的链路我们可以看出,业务网关后面就是我们的服务端系统,各模块之间使用 Dubbo 来进行交互,当然异步用的是 DDMQ,而当模块需要使用集团的中台服务时,我们使用的是 HTTP。模块内部还使用了线程池,也使用了 MySQL、Redis 等外部服务。


第一步,确定“全链路”应该包含链路(或顶级接口),所谓的全链路,它其实是一个相对的概念,在刚开始做全压时,我们主要是把线上的核心链路找出来,找到这些链路的顶级接口,这其实就是发压的主要入口。


第二步,确保压测标识在这些链路中传递以及处理,第二步是最难的也是最复杂的,我们要分析第一步中这些链路中如何有效安全的传递压测标识,压测标识是系统中用来区分压测流量还是线上正常流量的标识,我们要保证压测标识正确的传递和清除,否则可能导致严重的线上事故。这里将给出我们的做法,供大家参考,主要分四大部分:


  • 尽可能的对模块无侵入或低侵入


微服务架构下可独立你部署的模块数可能会非常惊人,任何能成功实时的技术方案都应该要求是对业务模块是无侵入或者是低侵入的,否则将影响方案的推广以及实施成本,我们为了做到这一点,打算直接在我们的基础组件(内部使用的公共库和中间件)动刀子,尽可能的对用户透明。


  • 压测标识安全的传递和处理


这个要分模块内、模块间、模块外三个部分来考虑:


模块内:假如模块内部已经知道该流量是压测流量,我们如何保证该压测流量能在模块内部复杂的逻辑处理中不丢失?模块内主要考虑的是线程中和跨线程执行的时候,压测标识容易丢失,线程中,我们使用的是对 ThreadLocal 的包装类(我们没使用阿里开源的 TransmittableThreadLocal)。而为了能够跨线程传递,我们修改了 taxi-thread 公共库,将其中的 TaxiThreadPoolExecutor 等类进行了修改,加入了压测标识的传递(这里补充下背景,我们为了 traceId 能跨线程传递,在 taxi-thread 公共库中包装了 JDK 线程池相关的类,并在开发规范中要求研发同学不能直接使用 JDK 原生的线程池)。还有一块,就是日志打印,为了能准确区分压测流量和正常流量,也为了压测流量不污染线上数据(比如线上很多模块有埋点日志),我们修改了 taxi-log(我们这边没有直接使用 SLF4J,而是使用包装过的日志公共库 taxi-log),将压测流量所有的日志打在原日志目录下的 shadow 影子目录下,这一切对用户也是透明的。


模块间:我们这边模块间的通讯方式主要是 Dubbo 和 DDMQ,Dubbo 这块的话我们直接通过 Filter 来实现压测标识传递,而 DDMQ 本身就自带压测标识传递方式,可以直接使用。


模块外:这一部分主要是存储、缓存以及一些外部服务(比如上图的中台服务)。


存储例如 MySQL、MongoDB 等,我们必须要隔离压测和线上数据,所以我们会事先建好所谓的 影子表,影子表其实和线上表的区别就是表名,影子表会在真实表名前加一个 shadow_前缀,而我们的 taxi-mybatis、taxi-mongo 等公共库在识别到压测标识时,会给表或者文档名称前也带上 shadow_前缀。之所以只是做表隔离而没有做库级别隔离,考虑到的还是降低侵入性和成本。关于存储,还有一个关键点,假如模块只提供查询服务(比如某些配置中心),如果按照前面说的,存储接入压测标识这块做成无侵入的话,全压流量查询也会走影子表,这也许是我们不希望看到的,所以在 MySQL 这块我们特意做成有侵入的(需要加一个插件配置),否则默认不识别压测标识


对于分布式缓存,我们使用的是 Redis,这一块的处理方式和存储类似,我们修改了我们自己 Redis 包装的公共库,如果是识别到压测标识,默认在操作的 key 上加一个 shadow_前缀,保证压测流量不污染线上缓存数据。


对于外部服务,我们使用的是 HTTP 来调用,所以修改了我们 taxi-util 中的 HTTP 组件,做了压测标识的传递,保证下游外部服务能知道这是压测流量。那肯定有人问,如果下游服务不支持压测流量识别该咋办?所以这里我们借助了 SDS 服务降级系统https://github.com/didi/sds),可以只对压测流量进行拦截,使其不调用下游外部服务。


最后的效果如下:



第三步,确保全压流量能被监控到,这涉及到我们在实际全压中能否直观的感受到压测流量,这一块需要和内部的监控系统来打通,由于能方便的取到压测标识,这一块的实现我们不再阐述。


第四步,准备全压数据,确定接口调用比例,最理想的方式是能对线上流量进行克隆、放大和处理,作为压测输入数据来重放,但这块难度较大,需要有好的平台来支撑,我们目前只能使用更简单的方式来造数据。由于无法使用仿真数据,我们提前在影子表中造了一批用户、设备信息、位置等和业务相关的数据,然后去线上统计了链路上各顶级接口的流量和交易量的比例,来作为压测时流量放大的依据。当然,必不可少的还有一个发压工具或平台,例如滴滴的奥创发压平台。

确定全压目标和计划

全压前我们需要定下全压的目标,比如当前我们交易系统能支撑 100W 订单(现有日单量峰值),而业务今年的目标是冲击 300W 日订单,那按照峰值流量 2 倍来算,我们的交易系统需要支撑 600W 的单量,那么第一次全压的目标可以保守些,定为日订单 200W。因为哪怕线下验证已经充分,全压时也会遇到各种出乎意料的问题,当然发现问题其实也意味着我们发现了系统容量瓶颈,这也是全压的主要目的之一。全压计划也同样重要,因为我们系统一定是在不断的迭代中,上一次的全压结论可能会很快“过期”,所以我们需要定下明确的全压计划和节奏,不断降低全压的人力成本,使这一稳定性建设工作持续有效的进行下去。

4. 总结

截止到目前,我们已经进行过很多轮的全压,也在不断往全压中补充新的链路,加入新的模块,目前全压的人力成本还是较高,我们也在探索全自动化全压方案,到时候有成果将和大伙继续分享。


作者介绍


易振强,滴滴专家工程师


我是易振强,热爱开源,热爱分享,深耕分布式系统和稳定性建设,欢迎关注 SDS 服务降级系统:https://github.com/didi/sds ;也热爱生活,热爱漫画,一拳超人和海贼王都很好看!!


本文转载自公众号普惠出行产品技术(ID:pzcxtech)。


原文链接


https://mp.weixin.qq.com/s/2M9EyCIuT1mZ9drftdMEBA


2020-03-25 10:004182

评论

发布
暂无评论
发现更多内容

AI让远程交流“更清晰”:GAN消除视频通话中的抖动

吃透阿里大佬整理的Java面试要点手册,成功五面进阿里(二本学历)

Java架构追梦

Java 学习 架构 面试 核心知识点整理

容器化应用系统上生产的最佳实践

东风微鸣

Kubernetes 最佳实践 生产

机器学习是什么?

马同学

学习

DDIA 读书笔记(2)数据模型的存储与检索

莫黎

读书笔记

学习总结

饺子

架构师训练营第一周课后作业

李日盛

标准的开发框架,对企业开发有多重要?

Learun

敏捷开发 快速开发

Microsoft Azure机器学习采用NVIDIA AI为Word编辑器提供语法建议

架构师训练营第五周学习总结

尹斌

GitLab用户切换引发的某程序员“暴动”,怒而开源项目源码

小Q

Java git 学习 开发 代码仓库

数据湖探索DLI新功能:基于openLooKeng的交互式分析

华为云开发者联盟

数据 处理

1分钟带你get React setState 面试要点

Leo

面试 大前端 React setState

数据安全无小事:揭秘华为云GaussDB(openGauss)全密态数据库

华为云开发者联盟

安全 数据 加密

解析 CloudQuery 审计分析功能

BinTools图尔兹

数据库 sql 安全 工具软件

mPaaS x Menxlab | 1024程序员节:Talk is cheap,Show me the AppID

蚂蚁集团移动开发平台 mPaaS

程序员 开发者 mPaaS 1024

架构训练营第一周学习小结

李日盛

全面到哭!BAT内部Java求职面试宝典,必须人手一份!

Java架构之路

Java 程序员 架构 面试 编程语言

批处理 有状态等应用类型在K8S上应该如何配置?

东风微鸣

Kubernetes 最佳实践

Vidyo产品给用户方带来了什么直接的便利

dwqcmo

音视频 集成架构 解决方案 智能硬件

LeetCode题解:98. 验证二叉搜索树,递归中序遍历过程中判断,JavaScript,详细注释

Lee Chen

算法 大前端 LeetCode

iOS性能优化 — 一、crash监控及防崩溃处理

iOSer

性能优化 ios开发 Crash 监控及防崩溃处理

趣味科普丨一文读懂云服务器的那些事儿

华为云开发者联盟

镜像 服务器 服务

spring-boot-route(二十二)实现邮件发送功能

Java旅途

Java Spring Boot 发送邮件

面试官的灵魂一击:你懂 MySQL 事务日志吗?

Java架构师迁哥

学了那么多 NoSQL 数据库 NoSQL 究竟是啥

哈喽沃德先生

数据库 nosql 非关系型数据库

攻克金融系统开发难点,借助SpreadJS实现在线导入Excel自定义报表

葡萄城技术团队

SpreadJS 在线导入excel

千万不要往 Shell 里粘贴命令!

大道至简

命令行

ArCall功能介绍手册

anyRTC开发者

ios 音视频 WebRTC RTC 安卓

自动化测试框架类型,你知道几种?此处介绍5种比较常见的

软测小生

软件测试 自动化测试框架 软件自动化测试

JavaScript 类型 — 重学 JavaScript

三钻

Java 大前端

稳定性全系列(二):如何做线上全链路压测_架构_易振强_InfoQ精选文章