Java 使用 Google Guava 实现接口限流

一、引入依赖

<dependency>
    <groupId>com.google.guavagroupId>
    <artifactId>guavaartifactId>
    <version>30.0-jreversion>
dependency>

二、自定义注解及限流拦截器

自定义注解:@Limiter

package com.haitai.web.device.annotation;

import java.lang.annotation.*;
import java.util.concurrent.TimeUnit;

/**
 * 限流注解
 *
 * @Author xincheng.du
 * @Date 2023/7/5 17:13
 */
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Limiter {

    /**
     * 每秒创建令牌个数,默认:0.5
     * 代表每秒允许通过的请求数为0.5个,即2秒只能通过一个请求
     */
    double qps() default 0.5D;
 
    /**
     * 获取令牌等待超时时间 默认:500
     */
    long timeout() default 500;
 
    /**
     * 超时时间单位 默认:毫秒
     */
    TimeUnit timeunit() default TimeUnit.MILLISECONDS;
 
    /**
     * 限速提示信息
     */
    String msg() default "访问过于频繁,请稍候再试";
}

限流拦截器:RequestLimitingInterceptor

package com.haitai.web.device.interceptor;

import com.google.common.util.concurrent.RateLimiter;
import com.haitai.web.device.annotation.Limiter;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 限流拦截器
 *
 * @Author xincheng.du
 * @Date 2023/7/5 15:38
 */
@Component
@Slf4j
public class RequestLimitingInterceptor implements HandlerInterceptor {

    private final Map<String, RateLimiter> rateLimiterMap = new ConcurrentHashMap<>();

    @Override
    public boolean preHandle(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull Object handler) {
        if (handler instanceof HandlerMethod) {
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            Limiter rateLimit = handlerMethod.getMethodAnnotation(Limiter.class);
            // 判断是否有注解
            if (rateLimit != null) {
                // 获取请求url
                String url = request.getRequestURI();
                // 判断map集合中是否有创建好的令牌桶
                rateLimiterMap.put(url, rateLimiterMap.computeIfAbsent(url, key -> RateLimiter.create(rateLimit.qps())));
                RateLimiter rateLimiter = rateLimiterMap.get(url);
                // 获取令牌
                boolean acquire = rateLimiter.tryAcquire(rateLimit.timeout(), rateLimit.timeunit());
                if (!acquire) {
                    log.warn("请求被限流,url:{}", request.getServletPath());
                    throw new RuntimeException(rateLimit.msg());
                }
            }
        }
        return true;
    }

}

三、测试

package com.haitai.web.controller.device;

import com.haitai.common.annotation.Anonymous;
import com.haitai.web.device.annotation.Limiter;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @Author xincheng.du
 * @Date 2023/7/24 10:04
 */
@RestController
@RequestMapping("/test")
public class LimiterTestController {

    @Anonymous // 允许匿名访问
    @Limiter(qps = 0.2D) // 5秒一次
    @GetMapping("/limiter")
    public void limiter() {
        System.out.println("=============正常请求=============");
    }

}

5秒内第一次请求:
在这里插入图片描述
5秒内第二次请求:
Java 使用 Google Guava 实现接口限流_第1张图片

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