为什么要限流
在电商中经常有秒杀的场景,就是在某一瞬间会有高并发的产生。每一个API接口都有自己的访问上限,当接口的访问频率超过其承受范围时,为防止雪崩效应可以采用限流的方式给接口安上保险丝,保证接口的可用性。
具体样例
我这里先自定义作用于方法上的限流注解,然后用aop切面去拦截包含有自定义限流注解的接口,再结合谷歌开源工具包com.google.concurrent.RateLimiter类(令牌桶算法)来实现限流。
自定义RequestLimit注解
@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestLimit {
}
aop切面配置
@Component
@Scope
@Aspect
@Slf4j
public class LimitAspect {
//每秒只发出指定个令牌(这里方便测试用1个),此处是单进程服务的限流,内部采用令牌捅算法实现
private static RateLimiter rateLimiter = RateLimiter.create(1.0);
//Controller层切点 限流
@Pointcut("@annotation(com.chenyulian.aop.RequestLimit)")
public void requestAspect() {
}
@Around("requestAspect()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
Boolean flag = rateLimiter.tryAcquire();
Object obj = null;
if(flag){
obj = joinPoint.proceed();
}else {
log.info("哎哟喂,业务太火爆,稍等一会儿呗");
throw new Exception("哎哟喂,业务太火爆,稍等一会儿呗");
}
return obj;
}
}
说明:没有获得令牌抛出异常,配合全局异常捕获直接返回。
核心代码就上面两部分,剩下的只需要在接口上加上@RequestLimit注解就可以了
@RestController
@Slf4j
public class TestLimitController {
@GetMapping(value = "/testLimit")
@RequestLimit
public R testLimit() {
log.info("开始测试限流");
return R.ok("success");
}
}
在浏览器里快速刷新会出现