目录
为什么需要限流
限流顾名思义,就是限制突发大量流量,让系统以平稳安全的速度去处理请求流量
限流是保障服务的一种手段
好的限流策略既不会打垮服务,也能保证系统的吞吐量
通常来说,调用别人的核心接口需要考虑限流,自己设计的接口也要考虑限流
两大限流算法
【漏桶算法】
把请求比作是水,水来了都先放进桶里,并以限定的速度出水,
漏桶算法可以很好的控制流量的访问速度,一旦超过该速度就拒绝服务
漏桶算法的核心在于进水速度和出水速度的大小关系
【令牌桶算法】
假如用户配置的平均发送速率为r,则每隔1/r秒一个令牌被加入到桶中(每秒会有r个令牌放入桶中);假设桶中最多可以存放b个令牌。如果令牌到达时令牌桶已经满了,那么这个令牌会被丢弃;当一个n个字节的数据包到达时,就从令牌桶中删除n个令牌(不同大小的数据包,消耗的令牌数量不一样),并且数据包被发送到网络;如果令牌桶中少于n个令牌,那么不会删除令牌,并且认为这个数据包在流量限制之外(n个字节,需要n个令牌。该数据包将被缓存或丢弃);算法允许最长b个字节的突发,但从长期运行结果看,数据包的速率被限制成常量r。对于在流量限制外的数据包可以以不同的方式处理:(1)它们可以被丢弃;(2)它们可以排放在队列中以便当令牌桶中累积了足够多的令牌时再传输;(3)它们可以继续发送,但需要做特殊标记,网络过载的时候将这些特殊标记的包丢弃。
Guava RateLimiter
https://github.com/google/guava
guava rateLimiter 使用的限流算法是令牌桶算法
依赖
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>21.0</version>
</dependency>
Demo1
/**
* @author: wangmingxin03
* @date: 2020-07-27
*/
public class RateLimiterTest {
public static void main(String[] args) {
//新建一个每秒限制5个的令牌桶
RateLimiter limiter = RateLimiter.create(5);
System.out.println(System.currentTimeMillis() / 1000);
if (limiter.tryAcquire(20)) { //欠20个
System.out.println(System.currentTimeMillis() / 1000);
}
double waitTime = limiter.acquire(10); //阻塞
System.out.println(waitTime);
System.out.println(System.currentTimeMillis() / 1000);
/**
* 1595844686
* 1595844686
* 3.999002 约4s
* 1595844690
* */
}
}
Demo2
/**
* @author: wangmingxin03
* @date: 2020-07-27
*/
public class SmoothBurstyTest {
public static void main(String[] args) {
RateLimiter limiter = RateLimiter.create(5);
while (true) {
System.out.println("get 1 tokens: " + limiter.acquire() + "s");
}
/**
* 基本上是0.2s执行一次
*
* get 1 tokens: 0.0s
* get 1 tokens: 0.198228s
* get 1 tokens: 0.19413s
* get 1 tokens: 0.196393s
* get 1 tokens: 0.197964s
* get 1 tokens: 0.196329s
* get 1 tokens: 0.196447s
* get 1 tokens: 0.1957s
* get 1 tokens: 0.197344s
* get 1 tokens: 0.195927s
* get 1 tokens: 0.194946s
* get 1 tokens: 0.196722s
*
* */
}
}
Demo3
/**
* @author: wangmingxin03
* @date: 2020-07-27
*/
public class SmoothBurstyTest1 {
public static void main(String[] args) {
RateLimiter r = RateLimiter.create(5);
System.out.println("get 5 tokens: " + r.acquire(5) + "s");
System.out.println("get 1 tokens: " + r.acquire(1) + "s");
System.out.println("get 1 tokens: " + r.acquire(1) + "s");
System.out.println("get 1 tokens: " + r.acquire(1) + "s");
System.out.println("get 1 tokens: " + r.acquire(1) + "s");
System.out.println("get 1 tokens: " + r.acquire(1) + "s");
/**
* 第一个acquire(5) 马上返回
* 但是后面的acquire(1) 需要等待1s,替上一个请求还债
* 再到后面的acquire(1) 又趋于平稳
*
* get 5 tokens: 0.0s
* get 1 tokens: 0.998554s
* get 1 tokens: 0.193392s
* get 1 tokens: 0.198538s
* get 1 tokens: 0.194447s
* get 1 tokens: 0.198013s
*
* */
}
}
Demo4
/**
* @author: wangmingxin03
* @date: 2020-07-27
*/
public class SmoothWarmingUpTest {
public static void main(String[] args) {
RateLimiter rateLimiter = RateLimiter.create(2,
3, TimeUnit.SECONDS);
System.out.println("get 1 tokens: " + rateLimiter.acquire(1) + "s");
System.out.println("get 1 tokens: " + rateLimiter.acquire(1) + "s");
System.out.println("get 1 tokens: " + rateLimiter.acquire(1) + "s");
System.out.println("get 1 tokens: " + rateLimiter.acquire(1) + "s");
System.out.println("get 1 tokens: " + rateLimiter.acquire(1) + "s");
System.out.println("get 1 tokens: " + rateLimiter.acquire(1) + "s");
System.out.println("get 1 tokens: " + rateLimiter.acquire(1) + "s");
System.out.println("get 1 tokens: " + rateLimiter.acquire(1) + "s");
/**
* 起初 速率较慢,慢慢提升,趋于稳定 0.5s一个令牌
*
* get 1 tokens: 0.0s
* get 1 tokens: 1.331792s
* get 1 tokens: 0.994073s
* get 1 tokens: 0.661631s
* get 1 tokens: 0.495211s
* get 1 tokens: 0.495872s
* get 1 tokens: 0.495754s
* get 1 tokens: 0.495933s
* */
}
}
看看这4个demo,敲一下,再跑一下,
对RateLimiter
的使用就简单了解了
google
写的代码还是很牛逼的
RateLimiter
是对单机版的QPS限流
转载请注明:汪明鑫的个人博客 » Guava RateLimiter 简单使用
说点什么
您将是第一位评论人!