查看原文
其他

稳定性五件套-限流的原理和实现

编程一生 编程一生
2024-08-10

背景


最近了解到很多朋友对限流、熔断、降级、隔离、超时重试的概念和应用场景理解的不是很到位,所以想用五篇的篇幅稍微系统的介绍一下。


本篇是第一篇,是限流做详解,如果反馈好的话,我会继续写下面四篇。不好的话就算了,算我理解不够,再自己总结总结。


限流的概念


有朋友问我限流和熔断有什么区别,我的理解很简单。限流作用是防御上游流量超过处理能力的手段,熔断作用是容错下游的快速失败手段。


举个生活中的限流例子:


小A最近打算找个女朋友,他拜托了很多朋友帮自己介绍,朋友们也很给力,很多姑娘都愿意和小A聊一聊。小A发现时间忙不开了,他就制定了一个计划,一天见2个。这就是限流。


举个生活中的熔断例子:


小A在见这些姑娘的时候,如果有的姑娘不守时,超过约定时间半小时还没有出现,那小A就会离开。不然会耽误见下一位姑娘,这是一种熔断手段。另外,如果有的姑娘特别能说,聊天超过了3小时,小A也会打断姑娘,把姑娘先送走,不然也会耽误见下一位姑娘。这也是需要的熔断措施。


限流的原理


不管任何编程语言的实现,目前主流的底层就是基于令牌桶算法和漏斗算法。这两种算法达到的效果有所不同。


令牌桶算法


令牌桶算法是先有个固定容量的桶,一个任务会以固定的速率往桶里放token,请求来了会去取token。如果桶满了,token就溢出了。多出来的token就不要了。如果请求太快,token生产速度跟不上消费速率,桶空了,有的请求取不到token,这时候就会直接返回错误而不继续处理。


举个例子:


比如小A最后找到了心仪的女朋友小C。他俩相处融洽,一起包饺子吃。小A负责擀皮,小C负责包。小A会把擀好的皮放到一块案板上。这个案板可以放20张皮。如果皮擀多了,就放不下,这时候小A就会停下来等。如果皮擀的慢,小C没的包,也就只能停下来。这里的皮就相当于是token,包饺子就相当于是处理业务的请求。用图表示如下:


漏斗算法


漏斗算法也是先有个固定容量的桶,请求来了先经过桶,从桶里出去的速率是一定的。如果请求量让桶满了,多出来的请求就不处理了。如果桶是空的,新来的请求就能马上处理。


事实上,各种MQ比如kafka就是典型漏斗算法。broker就是这个固定容量的桶,生产者会不断的将数据写到broker里,消费者是采用的拉取模式,总是以固定的速率来消费。


令牌桶算法和漏洞算法的比较


限流的实现


基础实现


在Java中业界用的比较多的是Google出品的Guava RateLimiter和另外的一款resilience4j-ratelimiter实现限流。原理差不多。


下面以RateLimiter为例进行讲解。要实现一个限流总共需要用到RateLimiter的两个方法:


1>RateLimiter.create() 静态方法创建对象,初始化桶容量


2>acquire()或者tryAcquire()  获取请求token,两者使用一个即可。acquire方法是阻塞式的,用来实现漏斗算法;tryAcquire是非阻塞式的,用来实现令牌桶算法。


阻塞式是如果到达指定条件前一直不返回结果,通过下面的源码可看到内部实际上是用sleep来实现的阻塞。因为所有的请求获取权限时都会sleep固定的时间才返回,就达到了匀速的目的。


非阻塞是立即返回是否获取到权限(token)。这时候请求如果获取权限成功就处理请求,获取权限失败就直接返回一个自定义的快速失败处理方式。平时请求速率小于token产生速率,桶渐渐满了。一旦有突发流量,因为桶里有存量token,也可以直接获取到权限,就是为什么令牌桶算法可以应对突发流量的原理。


高阶实现


上面实现里讲的是工具组件,如果只使用工具组件有个问题。限流实际上需要定期进行容量评估,是一个动态的过程,如果只使用工具组件就需要每次修改代码。当然也可以将每个值写到一个统一配置里,比如zookeeper来进行管理。


如果规模大的情况下更好的一个解决方法是使用专门的平台。这个平台可以支撑更多维度的配置,比如集群维度的限流。集群维度和单机维度的区别是如果设置了一个总的阈值,系统可以根据机器资源情况自动计算出每台机器的限流情况。


在业界,阿里有个sentinel,有人称为微服务哨兵。它是一套更完整的生态,除了我上面提到的功能之外,还提供了动态系统保护、热点限流等功能。

继续滑动看下一个
编程一生
向上滑动看下一个

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存