一、微服务QPS限流:漏桶和令牌桶

漏桶算法:

以固定速率从桶中流出水滴,以任意速率往桶中放入水滴,桶容量大小是不会发生改变的。

——类比于古代的滴漏计时。滴漏会按照固定的速率从洞中滴水(出桶速率固定),然后让带有标有时间刻度的竹筹浮起来,从而达到计时的目的。负责的太监会不定时观察桶中剩余的水量,水多了就少加水或不加水,水少了就多加水(入桶速率不固定);如果水桶满了,那当然就不需要加水了(走接口拒绝或服务降级),溢出的水都是被拒绝掉的请求。

令牌桶算法Token:

令牌桶分为2个动作:动作1(服务端固定速率往桶中存入令牌)、动作2(客户端如果想访问请求,先从桶中获取token)。

以规定的速率往令牌桶中放入 token,用户请求必须获取到令牌桶中的 token才可以访问我们的业务逻辑方法,如果没有从令牌桶中获取到 token ,拒绝访问。在高并发情况下,如果我们的请求过多 超出了令牌桶生成令牌的速度,这时候请求就会被驳回,提示请稍后重试!

优势:能够控制请求的速率。

限流的目的:为了保护服务,避免服务宕机。

com.google.guava 提供的RateLimiter类来进行限流处理。

二、微服务降级和熔断

降级:

Server自身出现异常,或者由于资源有限需要对部分 Client 请求故意表现为异常(类似限流),为了不影响其他服务功能,主动关闭服务的一些功能。

—— 比如某个中间件微服务Spre(作用是预收集客户端请求,并根据业务请求类别,分发到不同的其他的具体微服务S0,S1,S2...去处理),假设它被客户端C0,C1,C2...疯狂调用,导致CPU陡增(可以作为降级的指标)。那么Spre可以捕获到该异常,并主动发起对自身的服务降级,限制某些功能(当然也需要有恢复的能力)。

简而言之,降级类似于把部分代码注释掉,牺牲部分功能。这要求 Spre 在实现时,需要具备感知异常的能力,并对关键逻辑实现开关控制。实际场景中,对异常的感知通常由熔断器实现。

熔断:

当 C 发现 S 出现异常(比如:大量超时),由 C 主动出击,暂停对 S 的请求。

—— 例如 铁路局C发现某条线路S出现故障,导致大量的其他线路延误超时,铁路局C可以对该条线路S设置熔断,使乘客无法再购买到该线路的车票。

当一个服务因为各种原因停止响应时,调用方通常会等待一段时间,然后超时或者收到错误返回。如果调用链路比较长,可能会导致请求堆积,整条链路占用大量资源一直在等待下游响应。所以当多次访问一个服务失败时,应熔断,标记该服务已停止工作,直接返回错误。直至该服务恢复正常后再重新建立连接。

C 对 S 熔断后,那么原本需要调用 S 实现的逻辑怎么办呢?C 可以使用 Mock 数据缓存数据缺省数据等替代,或者干脆就是抛异常返回错误信息(错误码)。此时,如果 C 也是一个微服务,它相当于做了服务降级。所以我们经常看到服务熔断和服务降级一起出现Spring Hystrix 的断路器在实现时就是把熔断和降级方案打包实现的)。

但本质上它们不是一回事,降级发生在 Server熔断发生在 Client(即使它也有可能是其他Client的Server)。之所以配合实现,是因为许多微服务模块同时承担 C 和 S 两种角色。