限流
限流是什么?韩国首尔梨泰院踩踏事件,一时刻大量人聚集在一个狭窄路口,最后导致事故的发生。假如果,进去的时候限流,出去的时候限流,严格管理,那么悲剧发生的概率是不是会小一点。
先问俩件事:
你的接口能支持多少qps?
假如1000个请求同时打在你的接口上,你的服务会发生什么事?
接口限流就是做力所能及的事,保证接口不被冲烂,超过阈值的请求,存在队列或者丢弃返回。

上图水龙头可以自己轻松设置水流的速度。
限流算法有哪些?
- 固定窗口
- 滑动窗口
- 漏桶
- 令牌桶
限流算法实战
单机限流
Google Guava 利用令牌桶算法实现了限流工具类 RateLimiter。
- 引入依赖
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>31.1-jre</version>
</dependency>
- 创建限流注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface RateLimit {
// 接口每秒多少qps
@AliasFor("qps")
double value() default 0;
@AliasFor("value")
double qps() default 0;
// 获取令牌的超时时间
int timeout() default 0;
// 获取令牌的超时时间单位
TimeUnit timeUnit() default TimeUnit.MICROSECONDS;
}
- 使用aop实现拦截
@Aspect
@Component
public class RateLimitAspect {
/**
* RateLimiter缓存
* different interfaces have different rate limiters
*/
private ConcurrentHashMap<String, RateLimiter> RATE_LIMITER_CACHE = new ConcurrentHashMap<>();
@Pointcut("@annotation(com.alibaba.anno.RateLimit)")
public void rateLimit() {
}
@Around("rateLimit()")
public Object pointcut(ProceedingJoinPoint point) throws Throwable {
MethodSignature methodSignature = (MethodSignature) point.getSignature();
Method method = methodSignature.getMethod();
// rate limiter cache key -> package name + method name (eg:"com.alibaba.comtroller.getUser")
String key = methodSignature.getDeclaringTypeName() + "." + method.getName();
RateLimit rateLimit = AnnotationUtils.findAnnotation(method, RateLimit.class);
if (rateLimit != null && rateLimit.value() > 0) {
double value = rateLimit.value();
int timeout = rateLimit.timeout();
TimeUnit timeUnit = rateLimit.timeUnit();
if (RATE_LIMITER_CACHE.get(key) == null) {
RATE_LIMITER_CACHE.put(key, RateLimiter.create(value));
} else {
RateLimiter rateLimiter = RATE_LIMITER_CACHE.get(key);
if (rateLimiter == null || !rateLimiter.tryAcquire(timeout, timeUnit)) {
// Custom processing logic, such as throwing exceptions
System.out.println(method.getName() + "接口速率限制");
}
}
}
return point.proceed();
}
}
- 使用
@RestController
@RequestMapping("/user")
public class RateLimitController {
@RateLimit(value = 1, timeout = 10, timeUnit = TimeUnit.MILLISECONDS)
@GetMapping("/limit1")
public String test1() {
return "Hello,world!";
}
@RateLimit(qps = 10, timeout = 10, timeUnit = TimeUnit.MILLISECONDS)
@GetMapping("/limit2")
public String test2() {
return "Hello,world!";
}
}
- 测试
接口请求速度过快,便会出现提示
