如何保证核心链路稳定性的流控和熔断机制?

首发公众号:码农架构
仅从设计优化、服务拆分、自动扩容等方面进行优化 , 有时候并不能完全解决问题 。 比如 , 有时流量增长过快 , 扩容流程还来不及完成 , 服务器可能就已经抗不住了
既然突发流量我们没法预测 , 业务上也不可能不依赖任何外部服务和资源 , 那么有什么办法能尽量避免 , 或者降低出现这些问题时对核心业务的影响呢?
流量控制01.流控常用的算法
目前业内常用的流控方法有两种:漏桶算法和令牌桶算法

  • 漏桶算法
“漏桶算法”的主要目的是控制数据注入到网络的速率 , 平滑网络上的突发流量 。 “漏桶算法”在实现上文如其名:它模拟的是一个漏水的桶 , 所有外部的水都先放进这个水桶 , 而这个桶以匀速往外均匀漏水 , 如果水桶满了 , 外部的水就不能再往桶里倒了 。 这里你可以把这些外部的水想象成原始的请求 , 桶里漏出的水就是被算法平滑过后的请求 。 从这里也可以看出来 , 漏桶算法可以比较好地控制流量的访问速度 。
  • 令牌算法
令牌桶算法是流控中另一种常用算法 , 控制的是一个时间窗口内通过的数据量 。 令牌桶算法大概实现是这样的:
  1. 每 1/r 秒往桶里放入一个令牌 , r 是用户配置的平均发送速率(也就是每秒会有 r 个令牌放入) 。
  2. 桶里最多可以放入 b 个令牌 , 如果桶满了 , 新放入的令牌会被丢弃 。
  3. 如果来了 n 个请求 , 会从桶里消耗掉 n 个令牌 。
  4. 如果桶里可用令牌数小于 n , 那么这 n 个请求会被丢弃掉或者等待新的令牌放入 。

如何保证核心链路稳定性的流控和熔断机制?文章插图
算法按一定速度均匀往桶里放入令牌 , 原始请求进入后 , 根据请求量从令牌桶里取出需要的令牌数 , 如果令牌数不够 , 会直接抛弃掉超限的请求或者进行等待 , 能成功获取到令牌的请求才会进入到后端服务器 。
与漏桶算法“精确控制速率”不太一样的是 , 由于令牌桶的桶本身具备一定的容量 , 可以允许一次把桶里的令牌全都取出 , 因此 , 令牌桶算法在限制请求的平均速率的同时 , 还允许一定程度的突发流量 。
算法按一定速度均匀往桶里放入令牌 , 原始请求进入后 , 根据请求量从令牌桶里取出需
02.全局流控
在分布式服务的场景下 , 很多时候的瓶颈点在于全局的资源或者依赖 , 这种情况就需要分布式的全局流控来对整体业务进行保护 。
业界比较通用的全局流控方案 , 一般是通过中央式的资源(如:Redis、Nginx)配合脚本来实现全局的计数器 , 或者实现更为复杂的漏桶算法和令牌桶算法 , 比如可以通过 Redis 的 INCR 命令配合 Lua 实现一个限制 QPS(每秒查询量)的流控组件 。
如何保证核心链路稳定性的流控和熔断机制?文章插图
一个需要注意的细节是:在每次创建完对应的限流 Key 后 , 你需要设置一个过期的时间 。 整个操作是原子化的 , 这样能避免分布式操作时设置过期时间失败 , 导致限流的 Key 一直无法重置 , 从而使限流功能不可用 。 此外 , 在实现全局流控时还有两个问题需要注意:一个是流控的粒度问题 , 另一个是流控依赖资源存在瓶颈的问题 。 下面我们分别来看一下 , 在实现全局流控时是如何解决这两个问题的 。
03.细粒度控制
首先是针对流控的粒度问题 。 举个例子:在限制 QPS 的时候 , 流控粒度太粗 , 没有把 QPS 均匀分摊到每个毫秒里 , 而且边界处理时不够平滑 , 比如上一秒的最后一个毫秒和下一秒的第一个毫秒都出现了最大流量 , 就会导致两个毫秒内的 QPS 翻倍 。
【如何保证核心链路稳定性的流控和熔断机制?】一个简单的处理方式是把一秒分成若干个 N 毫秒的桶 , 通过滑动窗口的方式 , 将流控粒度细化到 N 毫秒 , 并且每次都是基于滑动窗口来统计 QPS , 这样也能避免边界处理时不平滑的问题 。
自动熔断机制雪崩效应在多依赖服务中往往会导致一个服务出问题 , 从而拖慢整个系统的情况 。
为了便于管理和隔离 , 我们经常会对服务进行解耦 , 独立拆分解耦到不同的微服务中 , 微服务间通过 RPC 来进行调用和依赖:
如何保证核心链路稳定性的流控和熔断机制?文章插图