全链路压测保驾有赞双十一

2017年双十一即将来临,对于买家来说,是一年一度的购物狂欢,可以对于一个电商公司的技术来说,确是一年一次的大考。如何用更少的预算完成指定当前业务规模的流量高峰,是技术的永衡的主题。 我们有赞在双十一之前完成了全链路压测的方案,并把它用于大促的扩容和容量验证,取得了很不错的成果。

做过电商的技术同学都知道,在大促来临时,整个集群是的最高峰压力将是正常时间的几十倍,最高峰持续的时间会特别短,然后回落到正常水平的几倍的水平线上。所以,可能我们会自然而然地想到,把我们整个集群扩容几十倍的机器,在双十一当天应对几十倍的流量, 然后第二天就减至正常量,就可以完成大促的考验。事实情况是否真的这么简单?

大促保障的困难

用户购买商品的链路是一条很长很复杂的系统集群,中间会涉及到店铺,商品,会员,营销,交易,支付等6大核心模块,每个模块又会涉及来多个不同的服务化系统单元,我们把这一条骨干的链路就叫做核心链路。大家都知道,双十一当天,真正爆增的其实是买家的购买量,像开店/商品上架等等功能,其实并发量没什么变化。也就是说,真正的压力其实是在核心链路上面,如果把所有的系统都扩容几十倍,本身就是一个很大的浪费。正常来说,一个稍有规模的电商公司,日常有几千台机器维持正常的运转,本身就是一个较大的开销,如果突增几十倍的系统开销,对于公司的财务也是很大的压力。所以,一个较理想的方法,是只把核心链咱的系统扩大几十倍的系统吞吐量,就可以达到目标。

困难一:

大型的分布式系统,其实非常错踪复杂,公司会维持成千上成的服务化系统。理论上来说, 只有少部分的系统是核心链路的系统。但是在实际情况下, 因为公司人员的关系,会把某些非核心的系统,不知不觉加入到核心链路了。所以,第一件要做的事情, 就是把非核心系统,从核心链路上踢除。

困难二:

一般的公司,都会在线下搭建性能测试环境,在该环境下,我们的测试同学可以借助一些测试工具,去压单机单接口的性能。假如,店铺的首页面,我们在性能环境下,得出单机单接口的qps峰值是500, 是否意味着, 要达到10w的qps,我只需要设置200台机器就可以了呢?答案当然是否定的。 因为任何的页面接口都不是单独存在的,都会受公共资源的制约,如:DB,Redis,MQ,网络带宽等等。比如当店铺首页达到10W的QPS的时候,商品的详情页可能要达到8W的QPS了,也会消耗公共资源。当公共资源的能力下降时,势必会影响所有的系统。所以,通过单机性能算出来的理论值,跟实际情况会相差很远。

困难三:

正常来说, 任何大促都会有业务目标,这个目标,一般是拿gmv进行评估。但是我们在进行系统容量评估的时候,一般会想扩大多少台机器。 那么gmv跟核心链路各个系统之间的机器数量的转化关系是什么样的?

困难四:

做过大型的分布式系统的同学,可能都知道一个事实,整个集群的性能,其实是取决于接口的短板效应。而这个短板的接口,在正常的流量下,是不会显现出来的。只有集群的整体压力达到一定值的情况下,才会偶尔显现, 然后造成雪崩效应,拖累整个系统集群。所以,如何在大促之前,如何 把这些短板找到呢?然后把它们一个一个优化,这件事情显得非常重要。

困难五:

应用系统的扩容, 相比较是简单的,完成大促之后,可以很容易归还。但是DB等核心资源的扩容其实是不容易的,而且资源不可能归还(数据不可丢失)。

事实是检验真理的唯一标准,上面的五个困难,其实都可以用线上真实压测的办法去检验。 业内大型电商公司,会用全链路压测的方案去指导扩容的进程。有赞也不例外。今年双十一,有赞用该方案完成了对核心链路20倍的扩容,但是整个集群的规模只是扩大了一倍多一点。

有赞全链路压测的设计方案

全链路压测的目标是让双十一要发生的事情提前发生,并且验证在该情况下,系统的表现是良好的。做线上压测,有一个很重要的原则:线上系统,是不允许有脏数据的。

有赞的压测设计方案,可以用几个很简单的话做概括:
1. 压测流量落影子库,正常流量落正常库。
2. 压测不能把系统压崩溃。
3. 压测的模拟双下一流量最高峰时用户的购物行为,所以综合性的流量模型需要跟实际情况相符

全链路压测的总体设计:

有图有真相,我们先上图。

在上述图中,我们明显可以看到,全链路压测有几个很关键的部分。
1 数据工厂,负责造请求数据。
2 大流量下发器,产生很大的压力去压系统。
3 线上服务集群同时处理压测请求+正常请求。
4 压测流量落影子存储,正常流量落正常存储。
5 压测流量对于外部的依赖走mock服务器,正常流量走正常外部集群。
6 水位的检测,需要检测存储+线上应用服务器的健康度,并且能够干预流量下发。

关键模块的设计方案

数据工厂设计

数据工厂是压测的一个很核心的部件。最主要的是由hive表的集合+各种导入/导出的脚本组成。
数据工厂的目的:保存压测需要准备的所有的数据,数据需要做清洗,比如:
1. 商品未下架
2. 商品的库存无限
3. 营销活动的信息有效,未过期。
4. 店铺未关闭
5. 等等。
场景的定义:
场景的定义关系到数据的准备,正常来说,压测只会压随着买家人数的暴增,系统的压力立即压加的场景,我们把这个场景涉及到的系统叫做“核心链路”。

影子存储的设计与路由能力

DB, 路由方式由RDS提供。 存储可以有两种方式:
1. 影子表与正常表存在同样的instance,不同的schema中。这个方案不需要增加额外的存储开销,相对便宜,但是风险较高(把库压死了会影响线上业务)。
2. 影子表与正常表存在不同的instance,相同的schema中。
这个方案相对较贵,需要额外搭建DB集群。但是安全性较高。 我们选择的是第一个方案。

redis: 通过key值做区别。压测流量的key值加统一前缀。通过redis-client做路由。

hbase, 通过命名空间做隔离。影子空间加前缀,提供统一的hbase client做数据访问, 由该client做路由。

es,通过index名字做区别。影子的索引统一加前缀。提供统一的es client做数据访问,由该client做路由。

线上应用集群的变更

1 统一线上应用对于数据的访问(db+es+hbase+redis),提借统一的client。
2 由于线上的应用都是服务化工程,远程调用时,必须具备压测流量的标记透传能力。
3 线上的少部分应用,需要访问三方的服务,比如:物流,支付。 这些应用需要改造为压测的流量直接访问mock服务器。

全布式流量下发器设计与链路设计的能力

我们选用gatling作为我们的流量下发器实现。 数据文件的内容
每一种场景,都有不同的数据文件,数据文件由场景相对应的多种url组合而成。比如:我们本次压测会压 “无优惠的场景,秒杀场景,满减场景,拼团场景” 等等。无优惠的场景分为“店铺首页,商品详情页,加购物车页,下单页,支付页,支付成功页”等等。这个文件不涉及到漏斗较化率。一般来说,一个数据文件很大(至少是G级别的)。所以我们的数据文件的内容格式为:

  • 所有的数据文件
    • 无优惠的场景数据文件
      • 场景集合1
        • 店铺首页URL
        • 商品详情页URL
        • 加购物车页URL
        • 下单页URL
        • 支付页URL
        • 支付成功页URL
      • 场景集合2
        • 店铺首页URL
        • 商品详情页URL
        • 加购物车页URL
        • 下单页URL
        • 支付页URL
        • 支付成功页URL
        • 秒杀场景数据文件
        • 满减场景数据文件
    • 拼团场景数据文件
      • ....

流量下发脚本的内容
流量下发脚本, 核心是控制漏斗转化率。
1 不同场景的流量配比。
2 每个场景下面,url从上往下的漏斗较化率。
gatling提供天然的转化率配置脚本, 用起来非常方便。有兴趣的同学可以自行google。

水位检测系统的能力设计

这个是一个很重要的模块,在项目启动之初,是希望:以实时计算的方式, 一边采集各个应用系统的资源使用情况+接口耗时+业务正确率, 一边向gatling发送流量干预信号,以做到自动保护系统的目的。由于时间的关系,我们并未实现。取而代之的是人肉查看实时监控界面的方式,人为去干预gatling的流量下发情况。

全链路压测的实施

如果实施过全链路压测的项目,大家都会有一个共同的感受:做基础的组件容易,让核心业务去完成相关的升级与验证工作很难。原因只有一个:需要用全链路压测的公司,业务规模都很大,涉及的团队都会特别多。梳理清楚庞大的业务,让所有的业务团队一起发力,本身就是一件很难的事情。

我们把链路压测的实施分为以下几个阶段:
1 基础中间件开发,各种框架升级开发,压测器研究与脚本开发。
2 业务升级与线下验证(人工点击,数据落影子库)
3 业务升级与线上验证(人工点击,数据落影子库)
4 数据工厂数据准备。
5 小流量下发验证(用gatling下发,数据落影子库)
6 大流量压测与系统扩容
第2,3,5的阶段,都需要借助业务测试同学的力量。
第4的阶段,需要借助业务开发同学的力量。
第6的阶段,需要借助业务团队+运维同学的力量。

所以,每个阶段人员都不太一样,所以需要每一个阶段,都组织不同成员的虚拟小组,借助各个团队的力量完成相应的工作。

压测过程中的重要细节与把控

流量爬升的规律

正常来说,在大促之前做压测, 目的一般是给扩容/优化做方向性的指导。 假设,我们双十一,需要扩大20倍的容量,以应对高峰。那我一定不会一开始就拿20倍的流量去压我们的系统,因为这样做的话,所有的系统都会在一瞬间就挂掉, 这样没有任何意义。我们的做法是,阶段性的爬坡打流量,然后把系统的能力一段一段提升上去。

例如:
1 第一天,我们会以日常流量的最高峰,为起始流量,然后爬坡到一个流量高峰A,记为第一天的目标。在压测之前,先做一次扩容。在压测中,碰到了某个瓶颈了,加该系统的机器来提升能力。
2 第二天,我们以A为起始流量,然后再次爬坡到B。 同样压测前做扩容+压测中碰到瓶颈加机器。
3 以此类推,一直最终流量到目标流量为止。
4 每一天的压测,也是需要慢慢爬坡的方式提升流量。在爬坡的某个高度, 会稳定5分钟,然后再次爬坡。稳定时间5分钟,爬坡时间30秒。

非核心链路进核心链路的问题

发现并解决这个问题,本身就是压测的一个目的之一。 正常来说,非核心链路,在大促来临时,不会扩大多少的容量。 当压测的压力起来的时候,很容易通过系统报警查到。

当发现这个问题的时候,一定需要坚决地要求业务方做系统改造,把非核心系统的强依赖去掉。解耦的技术有很多,需要根据不同的业务规则来选择方案。比如:业务降级,通过中间件解耦,异步化等等。

上下游扩容/代码优化的选择

一般来说,在压测的过程中,当碰到压测流量不能再升高的时候,会有很多的原因,我们碰到的情况有以下几种:
1 下游的某些服务化工程的能力达到瓶颈了,导致网关rt值升高,拖累整个集群的QPS不上去。
2 网关应用自身的能力达到瓶颈。
3 中间件/DB 能力达到瓶颈。
4 job的能力达到瓶颈,导致数据处理不够及时。
5 流量集中的页面,消耗了集群大量的资源,如:店铺首页,商品详情页等。

针对2,3,4这样的情况,我们的选择是毫不犹豫地加机器,代码优化的性价比较低。 针对第1种情况,需要做一些分析,如果这样的能力是在系统设计者的预期之内的,可以选择加机器。如果完全超乎意料的,一定需要通过程序优化,来提升能力。不然的话,加了资源,可能还是瓶颈。
针对第5种情况,一定要做的事情,就是静态化。因为这些流量集中页面,一般都是展示性质的。不管如何做应用内的优化,获得的能力提升,远不如做静态化的收益大。

未来展望

全链路压测的方案只是在有赞初试牛刀,我们已经看到这个方案在提升+验证集群处理能力方面具大的价值。当前,这个方案做得还较粗糙,还存在一些问题:
1 压测只能在夜间做。
2 压测中需要有很多业务开发人员陪同。
3 链路规划复杂度太高。
4 压测控制台的稳定性还不够高。
5 水位检测与流量干预是通过肉眼来观看监控来实现。

后续我们团队会继续投入大精力去完善整个方案。希望可以做到,压测的方案可以变成:
1 线上测试链路的机器人,时时去检测线上系统的正确性,同时没有脏数据干扰。
2 测试同学手里的工具,做到流量压测常归化,开发同学不用陪同。
3 压测可以在白天进行,晚上熬夜毕竟不利于健康。
4 链路规划图形化,并与数据工厂结合, 完成数据的准备工作。
5 水位检测与流量干预来保护系统,让业务系统不会被压崩溃。