我们在工作中 有些接口访问量过大 为了保证服务的正常运行可以增加限流
第一步 引入aop和redis
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
第二步 注解实现
@Target(value = ElementType.METHOD)
@Retention(value = RetentionPolicy.RUNTIME)
public @interface Limit {
/**
* 资源的key,唯一
* 作用:不同的接口,不同的流量控制
*/
String key() default "";
/**
* 最大访问限制次数
*/
double permitsPerSecond();
/**
* 得不到令牌的提示语
*/
String msg() default "访问过多,请稍后再试.";
}
第三步 编写aop切面实现限流
@Slf4j
@Aspect
@Component
public class RateLimiterAspect {
@Resource
private StringRedisTemplate stringRedisTemplate;
@Around("@annotation(com.nms.common.utils.Limit)")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
Limit limit = method.getAnnotation(Limit.class);
if (limit != null) {
String key = "rate_limit:" + limit.key();
// 获取当前计数
String countStr = stringRedisTemplate.opsForValue().get(key);
long count = countStr == null ? 0 : Long.parseLong(countStr);
if (count >= limit.permitsPerSecond()) {
log.info("限流生效 - 方法: {}, key: {}", method.getName(), key);
throw new ServiceException(limit.msg());
}
// 计数器加1,并设置1秒过期
stringRedisTemplate.opsForValue().increment(key);
stringRedisTemplate.expire(key, 1, TimeUnit.SECONDS);
}
return joinPoint.proceed();
}
}
第四步 使用注解
@Limit(
key = "getByOrderNo",
permitsPerSecond = 1,
msg = "当前访问较多,请稍后再试!"
)
@GetMapping("getByOrderNo")
public RestResponse getByOrderNo(String orderNo) {
Map<String, Object> info = checkService.info(orderNo);
return RestResponse.success().put(info);
}
测试