Semaphore实现一个简单的限流器
class ObjPool<T, R> {
final List<T> pool;
// 用信号量实现限流器
final Semaphore sem;
// 构造函数
ObjPool(int size, T t){
pool = new Vector<T>(){};
for(int i=0; i<size; i++){
pool.add(t);
}
sem = new Semaphore(size);
}
// 利用对象池的对象,调用 func
R exec(Function<T,R> func) {
T t = null;
sem.acquire();
try {
t = pool.remove(0);
return func.apply(t);
}
finally {
pool.add(t);
sem.release();
}
}
}
// 创建对象池
ObjPool<Long, String> pool = new ObjPool<Long, String>(10, 2);
// 通过对象池获取 t,之后执行
pool.exec(t -> {
System.out.println(t);
return t.toString();
});
RateLimiter
Google开源工具包Guava提供了限流工具类RateLimiter
令牌桶算法
获取令牌接口
//阻塞等待获取
limiter.acquire()
//获取10个
limiter.acquire(10);
//非阻塞,尝试获取令牌,请求失败立即返回false
limiter.tryAcquire()
//尝试获取permits个令牌
limiter.tryAcquire(int permits)
//带超时时间
limiter.tryAcquire(long timeout, TimeUnit unit)
limiter.tryAcquire(int permits, long timeout, TimeUnit unit)
创建令牌接口
/**
* 创建一个稳定输出令牌的RateLimiter,保证了平均每秒不超过permitsPerSecond个请求
* 当请求到来的速度超过了permitsPerSecond,保证每秒只处理permitsPerSecond个请求
* 当这个RateLimiter使用不足(即请求到来速度小于permitsPerSecond),会囤积最多permitsPerSecond个请求
*/
public static RateLimiter create(double permitsPerSecond);
/**
* 创建一个稳定输出令牌的RateLimiter,保证了平均每秒不超过permitsPerSecond个请求
* 还包含一个热身期(warmup period),热身期内,RateLimiter会平滑的将其释放令牌的速率加大,直到起达到最大速率
* 同样,如果RateLimiter在热身期没有足够的请求(unused),则起速率会逐渐降低到冷却状态
*
* 设计这个的意图是为了满足那种资源提供方需要热身时间,而不是每次访问都能提供稳定速率的服务的情况(比如带缓存服务,需要定期刷新缓存的)
* 参数warmupPeriod和unit决定了其从冷却状态到达最大速率的时间
*/
public static RateLimiter create(double permitsPerSecond, long warmupPeriod, TimeUnit unit);
//如:每秒两个
RateLimiter rateLimiter = RateLimiter.create(2.0);
storedPermits
过去剩余的令牌
这个字段的设计是为了怕如果限流长时间没有使用,而流量突然进来,会使之前的令牌都没有使用到。
比如QPS = 5,create(5),即一秒5个请求,如果前600ms的令牌都没有使用。则storedPermits = 3,
后面的400ms请求了5个令牌,会把storedPermits的令牌以及freshPermits都给他。如果超过,需要等待。
就好像如果每秒产生一个令牌,前面的50s都没有流量,但是如果突然请求了100个令牌,却需要这个请求等待100s。应该将过去剩余的50个令牌给予使用,并且在剩余的50s内,边执行边等待。
freshPermits
现有的令牌