写点什么

从成都核酸系统崩溃,谈谈 IT 系统如何应对 10 倍以上流量冲击

作者:星汉未来
  • 2022 年 9 月 08 日
    北京
  • 本文字数:4095 字

    阅读完需:约 13 分钟

作者:胡忠想 星汉未来 联合创始人 &CPO 

​背景:现任北京星汉未来科技有限公司联合创始人兼 CPO,北京航空航天大学计算机本硕,2012 年毕业加入微博,2018 年晋升为技术专家,负责过微博全站热点应对、增长工具、Weibo Mesh 等项目。 微博 @古月中心相心,粉丝 60 万。著有极客时间专栏《从 0 开始学习微服务》,专栏有超 5 万订阅。

 

最近成都核酸系统崩溃的新闻成为热搜,让人想起了西安一码通崩溃时,笔者曾经写过一篇文章《从西安一码通崩溃,看千万级DAU系统该如何设计》来阐述千万级 DAU 系统应该如何设计。

同理对于核酸系统,在设计之初并未考虑到要承载千万级的流量,

那么该如何做才能快速应对一周后甚至一天后即将到来的 10 倍以上流量呢?以及一个系统应该如何设计才能具备扩展性,在需要紧急扩容的时候可以支撑 10 倍以上流量冲击呢?

本文将针对以上问题进行探讨,欢迎大家参与讨论。

 

一般应用系统的架构链路

在上一篇文章中,我介绍了一般应用系统的架构链路如下:

 

能否承载 10 倍以上的流量,要求首先要对系统当前的情况有定量的分析,可以用冗余度这个通用指标来衡量,而针对每一层冗余度的计算可以选用具体不同的指标。

  • 四层负载均衡通常使用连接数来评估处理能力,单台四层负载均衡的最大承载能力是固定的,因此可以将冗余度定义为单台负载均衡设备收到请求的最大连接数/实际连接数。

  • 七层网关是负责具体将进入机房的流量分发给后端的应用服务器,通常还会集中在这一层做一些鉴权、安全认证等工作,通常也用连接数来评估七层网关的处理能力,因此可以将冗余度定义为单台 nginx 的最大连接数/实际连接数。

  • ​应用服务器是具体来接收流量请求,它的承载能力一方面取决于请求量大小即 QPS,另一方面取决于请求的延迟即 RT,依靠其中任何一个单一指标都不能准确的评估出系统的压力情况,需要综合考虑请求量和延迟两个因素。为此我们提出了 MetricQPS 指标,它将应用的请求按照延迟分为不同的区间,再赋予不同区间的请求以不同的权重,延迟越高的请求权重越大,加权之后得到的总和我们就定义为 MetricQPS,相比 QPS 更加精准衡量系统承载的压力

 

 

而应用服务器的最大承载压力是多少呢?这个需要通过压测来确定。通常的压测分为两种:

1.1 单机压测

一种是单机压测,即通过 Jmeter 等工具构造请求对单机进行施压,在单机的响应延迟、资源利用率等指标达到阈值后停止压测。

1.2 全链路压测

一种是全链路压测,即模拟一条与线上业务完全一致的链路,包括机房流量入口到七层网关再到应用服务器最后到数据库服务器,然后通过录制线上流量并放大 N 倍,对全链路进行压测,从而得出各系统的瓶颈点。 

以上两种压测有利有弊,单机压测优点是实施成本低,流程简单,缺点是构造的请求一般无法模拟真实线上流量,所以压测的结果参考性不高。

全链路压测相反,优点是流量复制线上,链路复制线上,达到最大化程度模拟线上的效果,压测结果参考性高,缺点是实施成本高,需要搭建一条完整的请求链路,并且为了不对线上数据造成污染,还需要将压测流量的写入请求路由到与线上隔离的数据库,复杂度较高,所以一般多为规模较大的企业采用,并且只在特定时期才会去进行全链路压测,毕竟人力和机器成本都很高。 

那么有没有一种低成本的快速压测方法呢?这篇文章《通用场景下如何进行低成本快速压测》我们提出了一种低成本的快速压测方法。它的原理是为应用设定固定的 SLA,如请求的最大延迟、最大失败率或者最大慢速比等,在七层网关处逐步缩减可提供服务的应用服务器数量,从而逐步加大对后端的单台应用服务器的压力,一直到触发 SLA 时,即可得到系统中当前应用服务器所能承载的最大 MetricQPS。这样的话应用服务的冗余度即最大 MetricQPS/实时 MetricQPS。数据库服务器的冗余度一般可以用读写请求 QPS 来判定,以 MySQL 数据库为例,冗余度可以定义为单台 MySQL 最大读写 QPS/实时读写 QPS。

 

限流和降级 

有了各个层次的冗余度指标后,就可以清楚地知道各个层次的健康状况,如果要想应对 10 倍以上的流量,那么各个层次就要保证具备 10 倍以上的冗余度。一方面原有的系统并不是每一层都能够横向扩展,另一方面如果各层保持 10 倍以上冗余度的话,成本浪费就会十分严重。那么如何才能让系统快速具备临时支撑 10 倍以上流量的能力呢?

通常有两种手段,一种是限流,一种是降级。下面我们分别就这两种手段来详细论述。

2.1 限流

首先来看限流,限流可分人为的和自动的。人为限流一般是强干预,如核酸录入系统,可通过人为引导将不同地区的核酸录入流量分散到不同的时间段错峰进行,这样的话对核酸录入系统的流量可降低到原来的几分之一。自动限流则是根据系统的处理能力,主动地抛弃一部分流量,来保证系统一直可用。对于核酸录入系统,可根据系统的最大处理能力,为每一层设置限流策略,确保流量冲击时系统还能够持续运行。

2.2 降级

再来看降级,降级都是有损的,所以是临时性的手段,一般用于线上出现问题或者预判即将到来不可承受的流量时才会执行。

降级也需要分为等级,从对线上业务的影响程度严重与否,可以分为三级:

  • 一级是最严重的降级,为了能够应对 10 倍以上峰值流量的冲击,为系统提供足够的缓冲时间来应对,执行一级降级需要至少为系统能释放 5 倍以上冗余度。这类降级一般会涉及到核心系统功能,以推荐系统为例,一级降级对业务的影响一般是不再更新实时推荐,而是直接从缓存中获取静态数据,这样大大简化业务逻辑,降低性能损耗,为系统释放出足够的冗余度。

  • 二级是较严重的降级,执行二级降级需要至少为系统释放 1-2 倍的冗余。这类降级一般不会涉及到核心系统功能,继续以推荐系统为例,二级降级可以是参与排序的物料数量从上万条减少到一千条,这样也能简化业务逻辑,降低性能损耗。

  • 三级则是最轻微的降级,执行三级降级一般只会为系统释放不到 1 倍的冗余度。这类降级一般对业务影响不大,以推荐系统为例,三级降级可以是参与排序的物料数量减少一半,这样释放的冗余度有限,对线上业务逻辑影响也不大。

 

​总而言之,要想在短时间内不改变原有系统架构的情况下,需要做好两件事情:

一、在各层选取合适的指标来计算冗余度;

二、确立合理的限流和降级预案,并通过预案演练确定实际冗余度,才可以知道系统是否能够真正经受住 10 倍以上流量的冲击。

弹性扩容

如果限流和降级还达不到 10 倍以上冗余度的效果,就要考虑给现有系统进行扩容,把所有可以借调的资源扩容到线上,如一些日常闲置的测试服务器、过保的服务器甚至从一些不重要的业务借调服务器等。

然而无论是限流还是降级,对系统都是有损的。那么如何在保持合理的冗余度情况下应对 10 倍以上流量的冲击呢?最根本有效的办法还是做到系统可做到随时随地的弹性扩容。而不同层的扩容难度和挑战也差别很大,下面我们逐一展开。

  • 四层负载均衡扩容比较容易,单台负载均衡的最大连接数可达百万级,所以扩容成本也相对较低,重点是对机房出口带宽进行扩容,否则达到瓶颈时请求就会失败。但对于自建机房或者租赁机房来说,出口带宽扩容往往比较复杂,需要对网络设备进行升级和扩容,还会涉及设备采购、线路交割、路由调整等复杂的网络操作,所以需要提前规划并预留足够的冗余,临时扩容来不及。一种可行的方案是使用公有云机房,因为大部分公有云机房的出口带宽是充足的,并且按照使用流量收费,在需要的时候可以将流量切到公有云机房,并在公有云机房部署完整一套系统,做到可独立支撑流量访问。

  • 七层网关这一层扩容比较简单,可根据连接数计算冗余度,按照十倍冗余度以上进行机器扩容。

  • 应用层服务器的扩容需要进行流量压测以衡量应用的冗余度,再需要根据冗余度来计算需要扩容的服务器数量。这一层扩容的难点即算力调度,即如何快速获取算力以提供应用部署扩容。

对于私有 IDC 内部署的应用来说,因为私有 IDC 内采购的服务器一般是提前规划好的,按季度甚至按年进行采购,所以短时间内增加服务器是不现实的。为此需要一种算力共享池机制,能够在应用冗余度高的时候,可以将空闲的算力释放到共享池,在应用冗余度低的时候,又可以从共享池中获取,从而实现私有 IDC 内部的算力共享,达到资源共享的效果。 

算力共享池一般应用在两个场景:

一个是错峰调度,应用所需的资源往往按照峰值流量进行算力评估,所以大部分时间冗余度都比较高,算力存在浪费,所以可以将空闲的算力释放到共享池,如果有应用需要算力则可以从共享池中获取算力,实现错峰调度,在不增加机器成本的前提下,即可满足业务的需求,节省成本。

一个是在离线混部,一般情况下在线业务在白天对算力的需求较大,而离线业务在夜间对算力的需求较大,这样的话可以在白天将离线业务的算力调度到共享池,在线业务可以使用,而在夜间可以将在线业务的算力调度到共享池,离线业务也可以使用。

如果应用在公有云上部署的话,则算力调度十分便利,可随时随地从公有云获取算力。开源算力调度引擎 BridgX,具备在公有云上 1 分钟扩容 1000 台,10 分钟扩容 10000 台算力的能力。

如果应用采用混合云部署,则可将常规流量需要算力部署在私有 IDC,再借用公有云算力来应对峰值流量。

  • 传统的数据库服务器一般采用一主多从架构,扩容也只能扩容从库,所能承载的读请求最大值跟从库的数量成正比,而承担写请求的主库无法进行线性扩容。因此在设计数据库的时候,必须做好提前规划。通常的做法是把数据库按照一定的 hash 规则拆分为多个端口,这样的话对主库每个端口的写请求量就会大大降低。在单个数据库遇到写瓶颈时一个最有效的办法就是对数据库进行分库,如拆分为 16 个端口,则单端口的写请求量就是原来的 1/16,相比单个数据库的承载能力提升了 16 倍。

除此之外,还可以直接使用分布式数据库比如 tidb,支持数据库的水平扩容,无须考虑数据库增大或者访问量增大时的拆库问题。

Set 化部署

如果系统架构本身无法扩容,还有一种方案就是 Set 化部署。每个 Set 从四层负载均衡到七层网关,再到应用服务器,最后到数据库服务器都独立部署。把原有的用户数据按照一定的规则平均分配到每个 Set 内,每部分用户都只访问本 Set 内的服务,如果分为 10 个 Set 的话,则每个 Set 的请求量就只有原来的 1/10,每一层的压力也就只有原来的 1/10,承载能力也提升到原来的 10 倍。

用户头像

星汉未来

关注

还未添加个人签名 2022.02.14 加入

星汉未来致力于研发混合云和多云场景下的算力调度引擎、数据物流引擎及智能运维引擎等产品服务,作为一家云原生基础引擎提供商,用云原生引擎为企业赋能,帮助企业降本增效,提升业务竞争力http://galaxy-future.com

评论

发布
暂无评论
从成都核酸系统崩溃,谈谈IT系统如何应对10倍以上流量冲击_星汉未来_InfoQ写作社区