RateLimiter

com.google.common.util.concurrent.RateLimiter 谷歌下的限流工具,采用的是令牌桶算法

一些限流算法

1、令牌桶算法

原理:系统会以一个恒定的速度往桶里放入令牌,而如果请求需要被处理,则需要先从桶里获取一个令牌,当桶里没有令牌可取时,则拒绝服务。 当桶满时,新添加的令牌被丢弃或拒绝。允许一定程度的突发流量,若桶内令牌个数满足,并发请求可以同时获取,主要用于需要平滑突发限流的场景。

2、漏桶算法

原理:一个固定容量的漏桶,按照常量固定速率流出水滴,如果桶是空的,则不需流出水滴;可以以任意速率流入水滴到漏桶,如果流入水滴超出了桶的容量,则流入的水滴溢出了(被丢弃),而漏桶容量是不变的。即:请求可以任意速率进来,但是会以固定的速率被处理,不允许并发处理,主要适用需要平滑突发流入速率的场景。

3、计数器限流

原理:是比较常用的,主要用来限制总并发数,比如数据库连接池大小、线程池大小、程序访问并发数等都是使用计数器算法。

可以使用java.util.concurrent.atomic.AtomicInteger来控制并发次数,如:

public class CountAtomicTest {

private static AtomicInteger count = new AtomicInteger(0);

public static void main() {
    if (count.get() >= 5) {
        System.out.println("请求用户过多,请稍后再试!");
    } else {
        count.incrementAndGet();
        try {
            //处理核心逻辑
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            count.decrementAndGet();
        }
    }
 }
}

也可以通过使用信号量 java.util.concurrent.Semaphore 来控制并发执行次数,如:

public class CountSemaphoreTest {

    private static Semaphore semphore = new Semaphore(5);

    public static void main() {
        if(semphore.getQueueLength()>50){
            System.out.println("当前等待排队的任务数大于50,请稍候再试...");
        }
        try {
            semphore.acquire();
            // 处理核心逻辑
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            semphore.release();
        }
    }
}

二者区别:atomicInteger 将超过计数控制的请求直接拒绝,semaphore 将超过计数控制的请求放阻塞队列,阻塞队列超过一定大小可以拒绝请求。

com.google.common.util.concurrent.RateLimiter 用法简述

实现过程主要应用令牌桶算法

1、单线程场景,如下:

 public static void main(String[] args){

    //1s只放5个令牌 即200ms投一个令牌
    final RateLimiter rateLimiter = RateLimiter.create(5);
    try {
        Thread.sleep(500);
    }catch (Exception e){
        e.printStackTrace();
    }
    for (int i = 0; i < 50; i++) {
        long time1 = System.currentTimeMillis();
        //尝试获取令牌,返回等待的时间(s为单位)
        System.out.println(i + "尝试中...,需要等待:" + rateLimiter.acquire() * 1000);
        System.out.println(i + "已获得令牌,耗时:" + (System.currentTimeMillis() - time1));
        try {
            Thread.sleep(10);
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    System.out.println("所有已完成");
}

运行结果如下:

0尝试中...,需要等待:0.0
0已获得令牌,耗时:0
1尝试中...,需要等待:0.0
1已获得令牌,耗时:0
2尝试中...,需要等待:0.0
2已获得令牌,耗时:0
3尝试中...,需要等待:54.418
3已获得令牌,耗时:57
4尝试中...,需要等待:184.753
4已获得令牌,耗时:186
5尝试中...,需要等待:186.215
5已获得令牌,耗时:185
6尝试中...,需要等待:190.492
6已获得令牌,耗时:190
7尝试中...,需要等待:190.467
7已获得令牌,耗时:190
8尝试中...,需要等待:190.593
8已获得令牌,耗时:191
9尝试中...,需要等待:189.596
9已获得令牌,耗时:190
10尝试中...,需要等待:189.513
10已获得令牌,耗时:190
11尝试中...,需要等待:189.646
11已获得令牌,耗时:190
12尝试中...,需要等待:189.552
12已获得令牌,耗时:190
13尝试中...,需要等待:189.494
13已获得令牌,耗时:189
14尝试中...,需要等待:190.537
14已获得令牌,耗时:191
15尝试中...,需要等待:189.649
15已获得令牌,耗时:189
16尝试中...,需要等待:189.445
16已获得令牌,耗时:189
17尝试中...,需要等待:188.459
17已获得令牌,耗时:190
18尝试中...,需要等待:189.344
18已获得令牌,耗时:189
19尝试中...,需要等待:190.525
19已获得令牌,耗时:191
20尝试中...,需要等待:189.38
20已获得令牌,耗时:189
...
...

刚开始程序启动初始化 rateLimiter 开始每隔 200ms投令牌,所以前几个基本很快拿到了令牌,再往后基本是恒定速率拿到令牌。

2、并发场景如下:

public static void main(String[] args){
    //用countDownLatch模拟并发
    final CountDownLatch startLatch = new CountDownLatch(1);
    final CountDownLatch runLatch = new CountDownLatch(10);
    ExecutorService exe = Executors.newFixedThreadPool(10);
    //1s只放5个令牌
    final RateLimiter rateLimiter = RateLimiter.create(5);

    for (int i = 0; i < 10; i++) {
        final int j = i;
        exe.submit(() -> {
            try {
                //等待统一发号施令
                startLatch.await();
                long time1 = System.currentTimeMillis();
                System.out.println(j + "尝试中...,需要等待:" + rateLimiter.acquire() * 1000);
                System.out.println(j + "已获得令牌,耗时:" + (System.currentTimeMillis() - time1));
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                runLatch.countDown();
            }

        });

    }
    try {
        //等待700ms 使令牌桶中先有几个令牌
        Thread.sleep(700);
        System.out.println("即将发号施令");
        //发号施令
        startLatch.countDown();
        //等待所有完成
        runLatch.await();
        System.out.println("所有已完成");
    }catch (Exception e){
        e.printStackTrace();
    }
}

运行结果如下:

即将发号施令
0尝试中...,需要等待:0.0
0已获得令牌,耗时:0
4尝试中...,需要等待:0.0
4已获得令牌,耗时:0
8尝试中...,需要等待:0.0
8已获得令牌,耗时:0
1尝试中...,需要等待:0.0
1已获得令牌,耗时:0
5尝试中...,需要等待:23.738
5已获得令牌,耗时:26
9尝试中...,需要等待:222.748
9已获得令牌,耗时:224
2尝试中...,需要等待:402.048
2已获得令牌,耗时:401
6尝试中...,需要等待:600.35
6已获得令牌,耗时:599
3尝试中...,需要等待:800.318
3已获得令牌,耗时:800
7尝试中...,需要等待:1000.302
7已获得令牌,耗时:998
所有已完成

可以看到 0,4,8,1基本同时拿到令牌,后面的基本等待 200ms 才能取到令牌继续执行。只要令牌桶中有足够数量的令牌,可以允许一定程度的并发,比如秒杀场景的限流。

了解RateLimiter 的更多方法:http://ifeve.com/guava-ratelimiter/

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,539评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,911评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,337评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,723评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,795评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,762评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,742评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,508评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,954评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,247评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,404评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,104评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,736评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,352评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,557评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,371评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,292评论 2 352

推荐阅读更多精彩内容