评论

收藏

[PHP] TSF微服务治理实战系列(三)——服务限流

开发技术 开发技术 发布于:2022-08-26 13:27 | 阅读数:262 | 评论:0

一、导语
大家应该都有去游乐园游玩的经历,其实服务限流与游乐园人流管理很相似。比如每一个游乐园所能承载的标准游客总数是大概确定的,当游乐园承载的游客数量超出了标准数量,游客在游玩的时候就会出现游玩路线人潮拥挤(请求拥堵处理慢)、热点游乐设施排队久(热点API过载)、餐品饮料供应缺货(数据库连接池不足)等情况,更有在重大节日时由于人数太多导致的踩踏事故(服务宕机导致的雪崩)。
服务限流其实就是一种应对超额流量的保护机制,当业务流量超出系统能够承载的上限时,快速处理超额的请求(如快速失败),防止超额的请求继续争抢/占用系统资源。本节将简单介绍服务限流针对的问题域、设计原则及TSF在服务限流方面的能力和使用场景。
二、作者介绍
崔凯
腾讯云 CSIG 微服务产品中心产品架构师
多年分布式、高并发电子商务系统的研发、系统架构设计经验,擅长主流微服务架构技术平台的落地和实施,目前专注于微服务架构相关中间件的研究推广和最佳实践的沉淀,致力于帮助企业完成数字化转型。
三、限流概述
问题域
社会的优势资源总是稀缺的,稀缺的资源又总是让人蜂拥而至。比如在12306抢春节回家的票,就是一场全国返乡人民都会参与的秒杀活动,如此大规模的流量让春节回家难这件事年年上热搜,那么具体是什么技术原因导致的呢?
      
  • 由于回家心切而过于激动的去重复刷新抢票页面(海量并发读);
      
  • 票放出的时间和库存都是固定的,所有人会在同一时间点击购买按钮(海量并发写);
      
  • 很多人就想抢到好时间段的票,就去买了抢票机器人(流量放大);
      
  • 系统没有做多级缓存、数据精简、消息队列解耦等优化操作(自身架构问题)。

上述原因只是一部分主要影响因素,在遭遇大流量时,如果没有适当的防御机制,轻则系统响应缓慢、频繁报错,重则系统瘫痪。其实每年的车票就那么多,与其让所有人都花费额外的资源去争抢,不如早早让用户知道卖光了,快速失败。
服务限流就是为了保护后端服务和数据不被大流量击垮而设计,当发生流量浪涌时,让实例能在最大有效负荷运行,而不会超过负载导致崩溃。
能力模型
产品的能力再炫酷,也需要落地在问题场景中才能产生价值。那么针对上述问题场景,服务限流可以使用哪些能力模型来解决问题呢?小编认为至少需要具备如下几种主要的能力:全局限流从整体限定服务容量(包括网关和业务服务),标签限流进行精准的细粒度控制,动态调节让服务可以根据自身状态自适应调整流量。
全局限流
从每个服务的视角整体把控服务容量,并且不区分调用请求的来源。所有流经被调用服务的请求都将被统计计数,一旦统计数字超过了全局限流配置的阈值,整个服务会拒绝超过配额的请求。
标签限流
通过标签化能力,将上游请求进行分类,针对不同种类的请求配置不同的限流规则,同时限流规则甚至可以精确控制到每个请求上。最终通过多个限流规则的组合,实现针对服务中不同API、不同流量来源的组合型防护。
动态调节
针对单个实例基于自身情况和算法,可以动态调整流经请求数量,保证在不超过每个实例最大吞吐量的情况下,使得资源被有效的充分利用。
限流算法
限流常见的算法包括固定窗口算法、滑动窗口算法、漏斗算法、令牌桶算法等,算法的内容在互联网已有诸多描述,小编不再赘述,只简要对比各算法优劣点。
算法名称    优点    缺点              固定窗口算法    代码逻辑简单易实现    窗口临界点流量翻倍问题          滑动窗口算法    避免了临界点翻倍问题    超过临界值后没有缓冲机制          漏斗算法    固定速率保证稳定性,有一定缓冲    瞬时流量不能在短时间内立即被处理          令牌桶算法    兼顾缓冲机制和瞬时流量处理    初次启动时令牌数量需优化其实每种算法都有使用的场景,比如滑动窗口算法,实现起来简单易懂,比较适合简单的限流场景;漏斗算法由于其流出速率恒定的特点,更偏重于保护下游服务,减少大流量对下游服务或三方平台的冲击;令牌桶算法更注重保护应用自身,同时满足了对请求进行缓冲和瞬时大流量问题的平衡。
另外在微服务体系中,限流更多的落地方式,更多的落地方式是大量微服务共用一套分布式限流框架,方便统一配置、统一运维、统一管理。TSF服务限流通过令牌桶算法,实现了一整套分布式服务限流的管控机制,使得应用在引用TSF-SDK后,开箱即用的获得分布式限流的能力。
DSC0000.jpg

上图为TSF服务限流架构示意图,首先支撑端限流中心基于用户的限流配置,计算得出服务中每个实例单位时间(1S)内应经过的最大的请求数配额(单位时间内最大流经请求数),并将该配额下发到每一个对应的服务实例中(各实例配额并不相同)。其次TSF-SDK会将单位时间内的统计数据上传到限流中心,供限流中心计算下一个单位时间应当下发的配额。最后,在当前单位时间内,当超过配额的请求到达实例后,就会被拦截并返回HTTP 429(Too Many Requests)错误。
简单总结下,TSF服务限流通过SDK实时上报的实例统计数据,使得限流中心组件可以动态的调整每个实例当前的配额数值。例如一个服务有4个实例,全局限流配置为100QPS,则每个实例初始时各得25的配额。但当某一个实例发生阻塞,该阻塞实例会通过上报数据被限流中心感知,之后分配给阻塞实例的配额会适当减少(如5),并适当增加其它实例的压力。阻塞实例在低流量压力情况下逐渐恢复后,限流中心可以捕获到实例的压力已经减小,又会重新调整每个实例的配额到基本均分状态。
限流原则
以下图为例是一个通用的WEB系统架构,流量通过APP、PC、第三方平台等不同的入口经过网关的安全鉴权和攻击防护,通过CLB负载到每一个微服务网关暴露的对外API上,再由微服务网关将请求分发到不同的后端服务中。
DSC0001.png

从设计服务限流的视角,我们不应仅仅将限流的动作局限在某一些服务上,需尽量从整个架构的视角来分析,核心思想是 “分层分级”
分层是指请求从网关进入到返回用户,经过了网关、LB、微服务网关、后端服务等多层环节,我们其实可以在请求流经的每一层进行缓存,提前减少不安全的(如恶意攻击)、非必要的(如静态资源)的请求直接透传到后端服务和DB,避免宝贵资源被浪费。
分级是指用户请求会不同程度的分散到网关、前台服务、中台服务中的各个API中,那么根据服务是否核心、API是否热点等不同的特征,可以对各微服务进行分级。例如对于入口型的微服务网关或者BFF聚合服务,更适合配置针对网关/服务的全局限流;核心服务的核心API更适合配置针对API的标签限流;针对单个服务中API数量较多的情况,单独配置API可能不切实际,更适合通过全局限流配置一个该服务QPS合计的预估峰值。
另外,在配置限流之前,对微服务的历史数据(QPS均值和峰值、PV/UV等)、可搜集到的已有业务数据(用户数、库存、商家、预估流量等)、资源消耗情况(服务器配置和数量、CPU内存与流量的对应关系等)提前参照,也是重要的准确评估流量的手段。
压测方法
在进行服务限流配置之前,首先要了解服务所处的层级、服务的预估容量等情况,这就需要一套完整的压测方法来支撑。以下为一些项目中压测的落地经验,供各位读者参考。
首先,在准备压测前先了解一些压测的“潜规则”:
      
  • 尽量保证压测环境与生产环境的服务器配置、数量、型号保持一致;
      
  • 影响压测的因素包括不限于:实例数量、服务器配置、应用配置、网络环境等;
      
  • 通过改变上述某一个影响压测的因素,观察压测数据的变化趋势,而不要同时改变多个;
      
  • 当服务中待压测接口较多时,优先压测核心接口,因为时间总是有限;
      
  • 常见可能的拐点为,在并发压力增大时QPS持平或不升反降、应用报错率飙高、响应耗时飙高等;
      
  • 长链路压测过程中尽量保证被压服务的下游依赖服务资源相对空闲,如consumer->provider且consumer为被压服务时,需保证provider并无明显压力;
      
  • 可先通过mock的方式压测自身的服务容量,再进行全链路压测。

其次,对于服务中单个API的压测,目的在于确认每个API在不同场景下的QPS。对于全链路的压测,是以模拟真实业务链路为前提,将服务中单个API进行串联后,确认链路上所有相关API的QPS,两种场景是从不同的视角出发的。全链路压测的问题在于,当上游服务的API成为瓶颈时,下游API的性能再好也无从发挥且不易觉察,而单个API的压测恰好弥补了这一点。同时,基于对上下游API和整体链路的QPS分析比对,可以有效的减少链路中不必要的资源浪费。所以在压测用例中可以尝试如下用例组合:
      
  • 服务单实例:通过单实例压测,了解每一个单独的部署单元的接口/服务容量;
      
  • 服务多实例:单实例前增加一层客户端负载均衡,与单实例的压测数据对比,观察对接口的影响;
      
  • 网关单实例:同服务单实例,了解网关单实例容量;
      
  • 网关多实例:同服务多实例,了解网关多实例容量;
      
  • 网关+服务:通过增加网关,观察网关路由对接口的影响;
      
  • CLB+网关+服务:通过增加CLB,观察CLB对接口的影响;
      
  • CLB+网关+服务A+服务B+服务C:通过全链路压测,了解真实业务链路情况下服务运行的状态。

增加实例并不会线性增加容量,当进行服务容量评估时,需要以实际压测结果作为参考。另外,针对压测数值要留有一定的安全空间作为缓冲,通常限流配置的阈值需根据实例平均CPU在70-80%左右时的QPS表现来确定。如网关压测时QPS为10000时CPU跑满且响应耗时开始显著增加(性能拐点),同时在QPS为7500时,各实例CPU平均数值为70%,则配置限流阈值应为7500(服务API同理)。具体如下表所示:
应用类型    CPU100%压测数值    实际配置数值              网关    10000    7500          服务    200    150在压测过程中mock接口可能需要模拟真实调用的随机延时,可从业务分支拉出专门用于压测的压测分支,并对待压测接口进行mock修改,mock代码内容可参考如下代码的思路编写:
public String mockResponseTime() throws InterruptedException {
  int responseTime = 0;
  Random randomProportion = new Random();
  Random randomOffset = new Random();
  int proportion = randomProportion.nextInt(100);
  int offset = randomOffset.nextInt(5);
  if(proportion < 80){
    responseTime = calcTime(50,offset);
  } else if(proportion >=80 && proportion < 95){
    responseTime = calcTime(200,offset);
  }else {
    responseTime = calcTime(400,offset);
  }
  Thread.sleep(responseTime);
  return "OK";
}
private int calcTime(int milliSecond,int offset){
  Random random = new Random();
  if(random.nextInt(100) % 2 == 0){
    return milliSecond + offset;
  }
  return milliSecond - offset;
}
四、TSF限流
配置方法
TSF的服务限流包括两种配置方式:全局限流、标签限流。全局限流针对整个微服务的所有请求,标签限流可以根据上游流量携带的系统标签或自定义标签进行细粒度管控。限流的配置主要通过配置时间和请求数来控制。
DSC0002.png

一个服务可以有多个同时生效的限流规则,当一个请求被多个限流规则约束时,则仅当所有作用规则都判通过时才会放行。且请求通过后每个作用规则的 通行请求数+1。若被一个规则拦截,则针对该规则的 限流请求数+1
DSC0003.png

此外,TSF弹性伸缩机制可以通过观测QPS、响应时间、CPU使用率、内存使用率的监控数据,对服务实例数量进行动态水平扩缩,安全有效的减少运维成本。当瞬时大流量来临时,启用弹性伸缩组内的预留资源,尽可能承接用户的业务请求。配合容器自身快速弹性伸缩的特性,可以实现服务的秒级扩缩容。
DSC0004.png

配置弹性伸缩规则时注意CPU利用率、内存利用率、请求QPS、响应时间的判断取的都是关联部署组内所有实例对应指标的平均值。冷却时间是指在完成一次扩缩容之后,会间隔一段时间(即冷却时间),期间并不会再次判断和触发弹性伸缩。
DSC0005.png

不过,注意弹性伸缩与服务限流组合使用的场景。第一,弹性伸缩与限流同时监控QPS时,弹性伸缩的规则生效时间需要持续至少1分钟以上才会生效,而限流需要在1秒内完成限制,会导致QPS在超限后立刻被限流而达不到弹性扩容的条件或者已经超限至少1分钟后才开始限流;第二,弹性伸缩的目的在于动态改变实例的数量,而限流规则是根据固定的实例数量而配置的,当发生弹性扩缩容后,也就意味着原有配置的限流规则失效并需要更新。
电商大促
每年的双11大促是商家和用户们的狂欢,但细心的读者朋友可能会发现,其实不止11月,每个月电商平台都会招揽商家们搞促销。即使是为了应对3.15这种每年都会查处商家的晚会,各大平台仍会乐此不疲。所以,对于有一定规模的电商平台而言,系统为了应对每月促销所带来的巨大流量,限流几乎是研发团队的一项日常操作。
针对电商大促的场景,TSF可以通过全局限流+标签限流的组合来管理流量。首先,假设目前仅完成了核心服务consumer-demo的服务部署,未进行任何的限流配置。
DSC0006.png

在日常流量大幅波动时,核心服务没有自保机制,这是业务不能接受的,所以需要对核心服务添加限流规则。在经过压测及历史数据分析后,预估当前资源配置可支撑的容量为1500QPS,即全局限流配置每1秒1500个请求作为上限,如下图所示:
DSC0007.png

其次,仅配置全局限流不能针对性解决热点API超载的问题,那么可以配合标签限流针对热点API进行限制,如下图所示将某一个热点API限制在1000QPS以内。
DSC0008.png

额外补充一点,由于微服务网关/网关是南北流量的入口,一旦网关瘫痪或故障,影响的是所有的业务。如果不在网关处添加一定的限流规则,可能会发生请求还没有流经后端服务,网关已经先搞挂了,这样后端的限流策略根本无从起效。
五、结语
服务限流相比其他治理手段更容易在落地项目中看到,主要是由于其实现方式比较成熟,同时各行各业中有丰富的落地场景。TSF服务限流的主要价值在于,极少的代码改造成本和学习成本、可以统一管理不同语言及框架的限流策略、简单的控制台使用方法、免运维的支撑方式,让用户可以快速的获得稳定的服务限流能力。
另外服务限流也需要在未来适应更多的场景,比如跟降级规则的联动、如何做到更精准的流量预测和流量自适应、如何做到毫秒级限流等。随着微服务治理实战系列已经来到了第三章,也希望能更多的得到大家的反馈,一起探索更多的落地场景和方法。
引用
https://cloud.tencent.com/document/product/649/30719\
https://cloud.tencent.com/document/product/649/19046