写点什么

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

  • 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:004416

评论

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

一场关于元宇宙公司之死的剧本杀

白洞计划

初识JVM的内存结构

Ayue、

技术专题合集

Sinfonia: a new paradigm for building scalable distributed systems--翻译理解【1】

Krysta

分布式 transaction Sinfonia DSM 两阶段提交改进

使用工具Source Monitor测量您Java代码的环复杂度

汪子熙

Java 软件工程 28天写作 12月日更 代码复杂度

如何在Flutter应用程序中创建不同的渐变 【Flutter专题14】

坚果

flutter 28天写作 12月日更

短视频平台的风控系统设计

Bill Zhang

我不用“996”,更不用“007”,可我赚的就是比你多

六十七点五

软件测试 自动化测试 接口测试 测试工程师 功能测试

恒源云(GPUSHARE)_分享一个技巧!CV训练时容易忽视的数据标签问题

恒源云

深度学习 算法 CV

模块五作业

危险游戏

架构实战营

☕【并发技术系列】「多线程并发编程」技术体系和并发模型的基础探究(夯实基础)

码界西柚

Java 并发编程 多线程 多进程 12月日更

分享一个从源码快速构建应用的小工具

Draven Gorden

开发者工具 开发工具 开源项目

web技术分享| AudioContext 实现音频可视化

anyRTC开发者

Web 音视频 WebRTC 音频可视化 AudioContext

周边生态|RoP 重磅发布 0.2.0 版本: 架构全新升级,消息准确性达 100%

Apache Pulsar

Java 开源 架构 云原生 Apache Pulsar

PackML从会到不会——命令标签(4)

陈的错题集

标准化 PackML

云智慧正式开源运维管理平台(OMP),加速AIOps社区生态建设

云智慧AIOps社区

运维 运维监控 开源软件 运维体系 运维系统

小程序与H5适合的场景应用都有哪些

Speedoooo

ios开发 APP开发 容器安全 Andriod开发 容器应用

等保工作中常见导致测评结论为差的高风险项

行云管家

网络安全 等级保护 等保测评 等保结论

Cordova插件中JavaScript代码与Java的交互细节介绍

汪子熙

Java JavaScript 移动应用 28天写作 12月日更

带你了解Typescript的14个基础语法

华为云开发者联盟

typescript 数组 开发 js 语法

常用项目部署方案和区别

进击的梦清

DevOps 运维 项目 部署与维护

12 月亚马逊云科技培训与认证课程,精彩不容错过!

亚马逊云科技 (Amazon Web Services)

架构师 培训 认证

恒源云(GPUSHARE)_CIFAR-10数据集实战:构建ResNet18神经网络

恒源云

深度学习 算法

Selenium之css怎么实现元素定位?

六十七点五

大前端 软件测试 自动化测试 接口测试 selenium

基于星环科技大数据平台 辽宁城市建设职业技术学院打造智慧校园

星环科技

大数据

vCenter管理软件用什么牌子好?有哪些用处?

行云管家

虚拟化 vcenter

当我们谈论“远程开发”时,我们在谈论什么

Draven Gorden

云原生 开发者工具 开发工具 远程协作 开发环境

Java和ABAP中的几种引用类型的分析和比较

汪子熙

Java 引用 28天写作 abap 12月日更

MySQL探秘(二):SQL语句执行过程详解

程序员历小冰

MySQL 28天写作 12月日更

dart系列之:还在为编码解码而烦恼吗?用dart试试

程序那些事

flutter dart flutter 面向切面 aop 程序那些事 12月日更

Linux一学就会之Centos8用户管理

学神来啦

Linux centos 运维 linux云计算

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