限流的介绍以及限流的代码实现

限流的介绍以及限流的实现

什么是限流

限流就是对某一段时间窗口内的请求数进行控制,保持系统的可用性和稳定性。
防止因为流量的暴增而导致系统奔溃。

很多平台都有限流,例如我们调用第三方的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文档传送门

如果有帮助到你,就关注或者点赞吧。感谢~~

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