前言分布式环境下应对高并发保证服务稳定几招,按照个人理解,优先级从高到低分别为缓存、限流、降级、熔断,每招都有它的作用,本文重点就讲讲限流这部分 。
坦白讲,其实上面的说法也不准确,因为服务降级、熔断本身也是限流的一种,因为它们本质上也是阻断了流量进来,但是本文希望大家可以把限流当做一个单纯的名词来理解,看一下对请求做流控的几种算法及具体实现方式 。
为什么要限流其实很好理解的一个问题,为什么要限流,自然就流量过大了呗,一个对外服务有很多场景都会流量增大:
- 业务用户量不断攀升
- 各种促销
- 网络爬虫
- 恶意刷单
下面看一下常见的两种限流算法 。
漏桶算法漏桶算法的原理比较简单,水(请求)先进入到漏桶里,人为设置一个最大出水速率,漏桶以<=出水速率的速度出水,当水流入速度过大会直接溢出(拒绝服务):
文章插图
因此,这个算法的核心为:
- 存下请求
- 匀速处理
- 多于丢弃
- 无法面对突发的大流量----比如请求处理速率为1000,容量为5000,来了一波2000/s的请求持续10s,那么后5s的请求将全部直接被丢弃,服务器拒绝服务,但是实际上网络中突发一波大流量尤其是短时间的大流量是非常正常的,超过容量就拒绝,非常简单粗暴
- 无法有效利用网络资源----比如虽然服务器的处理能力是1000/s,但这不是绝对的,这个1000只是一个宏观服务器处理能力的数字,实际上一共5秒,每秒请求量分别为1200、1300、1200、500、800,平均下来qps也是1000/s,但是这个量对服务器来说完全是可以接受的,但是因为限制了速率是1000/s,因此前面的三秒,每秒只能处理掉1000个请求而一共打回了700个请求,白白浪费了服务器资源
令牌桶算法
令牌桶算法是网络流量整形(Traffic Shaping)和限流(Rate Limiting)中最常使用的一种算法,它可用于控制发送到网络上数据的数量并允许突发数据的发送 。
从某种意义上来说,令牌桶算法是对漏桶算法的一种改进,主要在于令牌桶算法能够在限制调用的平均速率的同时还允许一定程度的突发调用,来看下令牌桶算法的实现原理:
文章插图
整个的过程是这样的:
- 系统以恒定的速率产生令牌,然后将令牌放入令牌桶中
- 令牌桶有一个容量,当令牌桶满了的时候,再向其中放入的令牌就会被丢弃
- 每次一个请求过来,需要从令牌桶中获取一个令牌,假设有令牌,那么提供服务;假设没有令牌,那么拒绝服务
注意上面多次提到一定程度这四个字,这也是我认为令牌桶算法最需要注意的一个点 。假设还是1000QPS的速率,那么5秒钟放1000个令牌,第1秒钟800个请求过来,第2~4秒没有请求,那么按照令牌桶算法,第5秒钟可以接受4200个请求,但是实际上这已经远远超出了系统的承载能力,因此使用令牌桶算法特别注意设置桶中令牌的上限即可 。
总而言之,作为对漏桶算法的改进,令牌桶算法在限流场景下被使用更加广泛 。
RateLimiter使用上面说了令牌桶算法在限流场景下被使用更加广泛,接下来我们看一下代码示例,模拟一下每秒最多过五个请求:
public class RateLimiterTest {private static final SimpleDateFormat FORMATTER = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");private static final int THREAD_COUNT = 25;@Testpublic void testRateLimiter1() {RateLimiter rateLimiter = RateLimiter.create(5);Thread[] ts = new Thread[THREAD_COUNT];for (int i = 0; i < THREAD_COUNT; i++) {ts[i] = new Thread(new RateLimiterThread(rateLimiter), "RateLimiterThread-" + i);}for (int i = 0; i < THREAD_COUNT; i++) {ts[i].start();}for (;;);}public class RateLimiterThread implements Runnable {private RateLimiter rateLimiter;public RateLimiterThread(RateLimiter rateLimiter) {this.rateLimiter = rateLimiter;}@Overridepublic void run() {rateLimiter.acquire(1);System.out.println(Thread.currentThread().getName() + "获取到了令牌,时间 = " + FORMATTER.format(new Date()));}}}
推荐阅读
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 判断因果关系的向量时钟算法
- Oracle用户进程和后台进程详解
- 各种各样加密算法的js库,安全加密不再愁——crypto-js
- 什么是青砖茶,青砖茶的特点是什么
- Go垃圾回收之三色标记算法
- CentOS 7安装TCP BBR拥塞算法
- 神奇的数据恢复算法
- 史上最详细的Linux网卡ifcfg-eth0配置详解
- SEO算法演变与破解
- 什么是搜索引擎本地算法,它对SEO有那些影响?