【高并发系列】16、Guava的RateLimiter限流

限流的必要性:任何应用或模块组件中,如果请求速率超过了访问速率上限,多余的请求无法处理,严重时可能压垮系统导致所有请求都无法有效处理;

简单限流算法:在给定单位时间内,使用计数器统计收到的请求数量,超过上限时,多余的请求丢弃或等待;这种算法有问题,很难控制边界时间上的请求;假如单位时间t内请求上限为n,0~t/2时间内无请求,t/2~t请求n,t~3t/2请求n,3t/2~2t无请求,分别在0~t和t~2t请求都为n,但在t/2~3t/2这个单位时间内请求为2n;

漏桶算法:利用缓存区,不管请求速率如何,都现在缓存区保存,以固定的流速流出缓存区进行处理;缓存区大小及流出速率为重要参数;

令牌桶算法:在单位时间内产生一定量的令牌存入桶中,处理程序只有拿到令牌后才能对请求进行处理;如果没有令牌,要么丢弃请求,要么等待可用的令牌;

漏桶算法能够强行限制数据的传输速率;令牌桶算法能够在限制数据的平均传输速率的同时还允许某种程度的突发传输;

Guava的RateLimiter采用了令牌桶算法;

public class RateLimiterDemo {
	// 每秒处理2个请求
	private static RateLimiter limiter = RateLimiter.create(2);
	public static class Task implements Runnable {
		@Override
		public void run() {
			System.out.println(System.currentTimeMillis());
		}
	}
	public static void main(String[] args) {
		for (int i = 0; i < 9; i++) {
			// 控制流量
			limiter.acquire();
			new Thread(new Task()).start();
		}
	}
}

console

1550362424868
1550362425369
1550362425870
1550362426366
1550362426870
1550362427366
1550362427867
1550362428368
1550362428866
 

如果为了保证服务质量,更倾向于直接丢弃过载请求,从而避免系统无法处理可能的崩溃,使用tryAcquire()方法;

public class RateLimiterDemo {
	// 每秒处理2个请求
	private static RateLimiter limiter = RateLimiter.create(2);
	public static class Task implements Runnable {
		@Override
		public void run() {
			System.out.println(System.currentTimeMillis());
		}
	}
	public static void main(String[] args) {
		for (int i = 0; i < 9; i++) {
			if(!limiter.tryAcquire()) {
				continue;
			}
			new Thread(new Task()).start();
		}
	}
}

tryAcquire()方法不会阻塞,例子中1秒处理2个请求,即500ms产生一个令牌,由于for循环本身效率高,完全可以在500ms内完成,因此只产生一个令牌,输出一次,其余请求全部被丢弃;

1550362900492

猜你喜欢

转载自blog.csdn.net/hellboy0621/article/details/87436336