JAVA限流

一、限流的方式

1.计数器(counter)

单位时间内限制请求总数

2.滑动窗口(sliding window)

计数器的升级版,动态的单位时间限定(详情参见TCP滑动窗口协议)

JAVA限流_第1张图片

3.漏桶(leaky bucket)

漏桶则是按照常量固定速率流出请求,流入请求速率任意,当流入的请求数累积到漏桶容量时,则新流入的请求被拒绝;漏桶限制的是常量流出速率,从而平滑突发流入速率;

JAVA限流_第2张图片

4.令牌桶(token bucket)

令牌桶是按照固定速率往桶中添加令牌,请求是否被处理需要看桶中令牌是否足够,当令牌数减为零时则拒绝新的请求;令牌桶限制的是平均流入速率,并允许一定程度突发流量;

JAVA限流_第3张图片

二、优缺点对比

计数器简单,允许突发请求,无法控制单位时间内热点请求问题与相邻单位时间头尾过热问题

滑动窗口相对复杂,允许突发请求,可以控制相邻单位时间头尾过热问题,仍存在单位时间内热点请求问题(将单位时间拆分的越精细对头尾过热问题控制越好)

漏桶流量固定,不允许突发请求,不存在单位时间内热点请求问题与相邻单位时间头尾过热问题

令牌桶,允许突发请求,可以控制相邻单位时间头尾过热问题,仍存无法完全控制单位时间内热点请求问题


三、手动实现

1.计数器

1.1)简单计数器

AomicInteger atomicInteger = new AomicInteger();
Integer currentTotal = null;
try{
	currentTotal = atomicInteger.incrementAndGet();
	if (currentTotal > 50) {
		// return
	} else {
		// do something
	}
} finally {
	if (null != currentTotal) {
		atomicInteger.decrementAndGet(); 
	}
}

1.2)分布式计数器        

/**
 * 指定单位时间(s)内数量限制是否需要限制
 *
 * @param key			限制标识
 * @param unitTime		单位时间
 * @param limitQuantity	限制数量
 * @return
 */
public Boolean checkNeedLimiter(String key, Long unitTime, Long limitQuantity) {
	Long count = redisTemplate.opsForValue().increment(key, 1);
    // 首次统计,设置超时时间
	if (1 == count) {
		redisTemplate.expire(key, unitTime, TimeUnit.SECONDS);
	}
    // 单位时间内数量大于限定数量 返回超限
	if (count > limitQuantity) {
		return Boolean.TRUE;
	}
	return Boolean.FALSE;
}

2.滑动窗口

3.漏桶

4.令牌桶


四、RateLimiter

com.google.common.util.concurrent.RateLimiter

// SmoothRateLimiter 1s 内生成5个令牌 200ms 生成一个令牌
static final RateLimiter rateLimiter = RateLimiter.create(5);

public static void main(String[] args) throws InterruptedException {
	int totalInt = 0;
	for (int i = 0; i < 10; i++) {
		int randomInt = (int) (Math.random() * 10) * 10;
		totalInt += randomInt;
		System.out.println("执行时间:" + totalInt + "ms");
		Thread.sleep(randomInt);
		boolean acquire = rateLimiter.tryAcquire();
		System.out.println("是否获得令牌:" + acquire);
	}
}


运行结果
执行时间:50ms
是否获得令牌:true        首次
执行时间:130ms
是否获得令牌:false
执行时间:190ms
是否获得令牌:false
执行时间:200ms           第一个200ms一个
是否获得令牌:true
执行时间:260ms
是否获得令牌:false
执行时间:330ms
是否获得令牌:false
执行时间:410ms            第二个200ms一个
是否获得令牌:true
执行时间:440ms
是否获得令牌:false
执行时间:510ms
是否获得令牌:false
执行时间:520ms
是否获得令牌:false

你可能感兴趣的:(Java,java)