常见限流算法

常见的限流算法有:令牌桶、漏桶、计数器。

令牌桶

令牌桶算法:

  • 一个存放固定容量令牌的桶(假设容量为n),
  • 按照固定速率往桶里添加令牌(假设限速为10r/s,则按照100毫秒的固定速率往桶中添加令牌,当桶满时,新添加的令牌会被丢弃),
  • 请求过滤之后,需要先从桶里获取一个令牌,如果获取到令牌,则进行业务处理;如果获取不到则拒绝服务。

例子:Google开源项目Guava中的RateLimiter使用的就是令牌桶算法

漏桶

  • 一个固定容量的漏桶,按照固定速率流出水滴,如果桶是空的,则不需要流出水滴
  • 任意速率流入水滴到漏桶,如果流入水滴超出了桶的容量,则流入的水滴溢出(被丢弃)

例子:nginx的limit_req_zone 用来限制单位时间内的请求数,即速率限制,采用的漏桶算法 "leaky bucket"

令牌桶和漏桶算法比较:

  • 令牌桶是按照固定速率往桶中添加令牌,请求是否被处理需要看桶中令牌是否足够,当令牌数量为0时,则拒绝新的请求。
  • 漏桶则是按照常量固定速率流出请求,流入请求速率任意,当流入的请求数量累计到漏桶容量时,则新流入的请求被拒绝。
  • 令牌桶限制的是平均流入速率(允许突发请求,只要有令牌就可以处理,支持一次拿n个令牌),并允许一定程度的突发流量。
  • 漏桶限制的是常量流出速率(即流出速率是一个固定常量值,比如都是1的速率流出,而不能一次是1,下次是2),从而平滑突发流入速率。

计数器

        采用计数器实现限流有点简单粗暴,一般我们会限制一秒钟的能够通过的请求数,比如限流qps为100,算法的实现思路就是从第一个请求进来开始计时,在接下去的1s内,每来一个请求,就把计数加1,如果累加的数字达到了100,那么后续的请求就会被全部拒绝。等到1s结束后,把计数恢复成0,重新开始计数。

        具体的实现可以是这样的:对于每次服务调用,可以通过 AtomicLong#incrementAndGet()方法来给计数器加1并返回最新值,通过这个最新值和阈值进行比较。

        这种实现方式,相信大家都知道有一个弊端:如果我在单位时间1s内的前10ms,已经通过了100个请求,那后面的990ms,只能眼巴巴的把请求拒绝,我们把这种现象称为“突刺现象”。

应用场景:使用计数器来进行限流,主要用来限制总并发数,比如数据库连接池大小、线程池大小、秒杀并发数,只要全局总请求数或者一定时间段的总请求数达到设定阀值,则进行限流。这是一种简单粗暴的总数量限流,不是平均速率限流。

try{
    if(atomicLong.incrementAndGet() > 限流数){
        //拒绝请求
    }
    //处理请求
} finally {
   atomicLong.decrementAndGet();
}
		// 使用guava的Cache来存储计数器
		LoadingCache<Long, AtomicLong> count = CacheBuilder.newBuilder()
				// 设置过期时间是2秒,保证在1秒之内该计数器是可用的
				.expireAfterWrite(2, TimeUnit.SECONDS)
				//
				.build(new CacheLoader<Long, AtomicLong>() {
					@Override
					public AtomicLong load(Long seconds) throws Exception {
						return new AtomicLong(0);
					}
				});
		// 设置限速
		long limit = 5L;
		// 进行5波请求,每波请求请求数是10
		for (int loop = 0; loop < 5; loop++) {
			Thread.sleep(1000L);
			for (int i = 0; i < 10; i++) {
				// 得到当前秒
				long currentSeconds = System.currentTimeMillis() / 1000;
				if (count.get(currentSeconds).incrementAndGet() > limit) {
					System.out.println(currentSeconds + "限流");
				} else {
					// 业务处理
					System.out.println(currentSeconds + "业务处理");
				}
			}
		}

运行结果

1593227195业务处理
1593227195业务处理
1593227195业务处理
1593227195业务处理
1593227195业务处理
1593227195限流
1593227195限流
1593227195限流
1593227195限流
1593227195限流
1593227196业务处理
1593227196业务处理
1593227196业务处理
1593227196业务处理
1593227196业务处理
1593227196限流
1593227196限流
1593227196限流
1593227196限流
1593227196限流
1593227197业务处理
1593227197业务处理
1593227197业务处理
1593227197业务处理
1593227197业务处理
1593227197限流
1593227197限流
1593227197限流
1593227197限流
1593227197限流
1593227198业务处理
1593227198业务处理
1593227198业务处理
1593227198业务处理
1593227198业务处理
1593227198限流
1593227198限流
1593227198限流
1593227198限流
1593227198限流
1593227199业务处理
1593227199业务处理
1593227199业务处理
1593227199业务处理
1593227199业务处理
1593227199限流
1593227199限流
1593227199限流
1593227199限流
1593227199限流

参考https://blog.csdn.net/weixin_41846320/article/details/95941361

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章