什么是限流
限流就是对某一段时间窗口内的请求数进行控制,保持系统的可用性和稳定性。
防止因为流量的暴增而导致系统奔溃。
很多平台都有限流,例如我们调用第三方的api的时候,对方都会限制每天最多只能调用几次之类的。
常用的限流方式
1)信号量
2)令牌桶
信号量
Semaphore是一个计数信号量。常用于限制获取某资源的线程数量,
可用java的Semaphore 实现。
原理就是先定义总共有多少个信号量,然后每个线程进来拿到几个信号量才可以成功访问,这样就允许了同时有多少个线程访问。
例如,定义了6个信号量,每个线程进来拿2个,那么同时允许3个线程成功访问。
通过acquire()方法获取许可,该方法会阻塞,直到获取许可为止。
通过release()方法释放许可。
代码实现如下
@RestController
@RequestMapping("/semaphore")
public class SemaphoreController {
//初始化信号量=5,true-公平信号量FIFO。
private Semaphore semaphore = new Semaphore(5, true);
//该方法是阻塞的,直到获取许可为止
@RequestMapping("/acquire/{par1}")
public String acquire(@PathVariable String par1){
try {
semaphore.acquire(1);//获取一个许可
//
return par1+"获取一个许可";
}catch (InterruptedException e){
e.printStackTrace();
return "获取许可失败";
}catch (Exception e1){
return "获取许可失败";
}finally {
semaphore.release(1);//处理完后,释放一个许可
}
}
}
令牌桶
首先看下来自网络的图片
令牌桶法的过程大概如下:
1)程序按照一定的速度生成令牌,并且放到令牌桶中;
2)令牌桶容量是一定的,如果桶满了,就丢掉多余的令牌;
3)如果桶没满,就继续放令牌
4)请求过来的时候,拿到令牌就请求接口成功,拿不到令牌就拒绝访问。
令牌桶的实现谷歌的guava已经帮我们实现了,就是谷歌的 RateLimiter。
接下来,我们看下代码实现:
//每秒发放2个令牌
private static final RateLimiter rateLimiter = RateLimiter.create(2);
//以下是测试限流的接口
//一般是采用阻塞和非阻塞的方式来获取令牌,建议采用非阻塞。
/**
* 非阻塞方式
* 尝试获取令牌,默认超时时间是0,拿不到就立即返回false
*/
public String rateLimiterNonBlock1(){
if (rateLimiter.tryAcquire()) {//tryAcquire尝试获取令牌,默认超时时间是0,意思是拿不到就立即返回false。而且tryAcquire()只拿一个令牌
try {
//模拟操作。
Thread.sleep(500);
}catch (Exception e){
e.printStackTrace();
}
}else{//拿不到令牌
return "拿不到令牌,暂时无法访问。";
}
return "正常拿到了令牌,成功访问";
}
/**
* 非阻塞方式
* 尝试获取令牌,超时时间是1秒,如果在1000ms内无法获取令牌,则直接返回false,无需等待
*/
public String rateLimiterNonBlock2() {
//如果令牌桶内有令牌,则直接发放;判断如果能在1000ms内能够获取一个令牌,则等待到时后发放;如果在1000ms内无法获取令牌,则直接返回false,无需等待。
if (rateLimiter.tryAcquire(1000, TimeUnit.MILLISECONDS)) {
//do sth
return "获取令牌成功";
} else {
// do other
return "获取令牌失败";
}
}
/**
* 阻塞方式
* acquire拿不到就等待,直到一次拿到5个令牌为止
*/
public String reateLimiterBlock(){
rateLimiter.acquire(5); // 一次拿5个
return "";
}
注意:!!!!!
tryAcquire 并不是真正等待timeout时间,而是计算判断在timeout时间内能否拿到令牌,如果不能就返回false。这是一个挺复杂的数学问题,每一个请求都会被计算未来可能获取令牌的概率。还好谷歌帮我们实现了这个判断。
令牌桶也有大神封装好的可以直接用的,而且还封装有redis实现的分布式的限流方式。见下面传送门。
apiBoot项目传送门
apiBoot文档传送门
如果有帮助到你,就关注或者点赞吧。感谢~~