需求:单位时间内,比如1s,限制某个接口的调用次数。

RateLimiter

RateLimiter基于令牌桶算法,有一个令牌桶,单位时间内令牌会以恒定的数量(即令牌的加入速度)加入到令牌桶中,所有请求都需要获取令牌才可正常访问。当令牌桶中没有令牌可取的时候,则拒绝请求。
定义一个接口:

@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface RateLimit {    //每秒限制次数    double limit() default 1;}

定义一个切面:

@Slf4j@Component@Aspectpublic class RateLimitAspect {    private static final ConcurrentMap<String, RateLimiter> rateLimiterCache = new ConcurrentHashMap<>();    @Pointcut("@annotation(com.example.demo.RateLimit)")    public void rateLimit() {    }    @Around("rateLimit()")    public Object pointcut(ProceedingJoinPoint joinPoint) throws Throwable {        Object obj = null;        MethodSignature signature = (MethodSignature) joinPoint.getSignature();        Method method = signature.getMethod();        RateLimit rateLimit = AnnotationUtils.findAnnotation(method, RateLimit.class);        if (rateLimit != null) {            if (rateLimiterCache.get(method.getName()) == null) {                rateLimiterCache.put(method.getName(), RateLimiter.create(rateLimit.limit()));            }            RateLimiter rateLimiter = rateLimiterCache.get(method.getName());            if (rateLimiter != null) {                if (!rateLimiter.tryAcquire()) {                    log.info("=====完了没抢到...=====");                } else {                    log.info("=====我抢到了!!=====");                    obj = joinPoint.proceed();                }            }        }        return obj;    }}

controller:

@Slf4j@RestControllerpublic class TestController {    @RateLimit(limit = 5)    @GetMapping("/test")    public String test() {        return "请求成功";    }}

Redis

利用了Redis的自增和过期时间

@Slf4j@RestControllerpublic class TestController {    private int countLimit = 5;    private int timeLimit = 1;    @Autowired    StringRedisTemplate stringRedisTemplate;        @GetMapping("/test")    public String test(String key) {        if (stringRedisTemplate.hasKey(key)) {            long count = stringRedisTemplate.boundValueOps(key).increment(1);            if (count > countLimit) {                log.info("请求过于繁忙");            }        } else {            stringRedisTemplate.opsForValue().set(key, "1", timeLimit, TimeUnit.SECONDS);        }        return "请求成功";    }}