java应用单机版限流实现方案

1、引入依赖jar

<dependency>
            <groupId>com.github.vladimir-bukhtoyarov</groupId>
            <artifactId>bucket4j-core</artifactId>
            <version>4.10.0</version>
</dependency>

2、实现spring 拦截器

@Component
public class AccessLimitInterceptor extends HandlerInterceptorAdapter {

    private static Logger logger = LoggerFactory.getLogger(AccessLimitInterceptor.class);

    @Autowired
    private IApiInterfaceLogService interfaceLogService;

    @Autowired
    private CommonUtil commonUtil;
    
    /**
     * 接口请求限流
     */
    @Value("${my.request.limitSwitch}")
    private String limitSwitch;
    
     /**
     * 单机网关限流用一个ConcurrentHashMap来存储 bucket,
     * 如果是分布式集群限流的话,可以采用 Redis等分布式解决方案
     */
    private static final Map<String, Bucket> LOCAL_CACHE = new ConcurrentHashMap<>();
    /**
     * 桶的最大容量,即能装载 Token 的最大数量
     */
    @Value("${my.limit.capacity}")
    private int capacity = 5;
    /**
     * 每次 Token 补充量
     */
    @Value("${my.limit.refillTokens}")
    private int refillTokens = 2 ;

    /**
     *补充 Token 的时间间隔
     */
    Duration refillDuration = Duration.ofSeconds(1);

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 增加限流控制开关
        if (!"Y".equalsIgnoreCase(limitSwitch)) {
            return true;
        }
        long requestTime = System.currentTimeMillis();
        ResultVo resultVo = new ResultVo(ResponseStateCode.FAILED.getCode(), ResponseStateCode.FAILED.getMsg());
        try {
            // Handler 是否为 HandlerMethod 实例
            if (handler instanceof HandlerMethod) {
                // 强转
                HandlerMethod handlerMethod = (HandlerMethod) handler;
                // 获取方法
                Method method = handlerMethod.getMethod();
                // 是否有AccessLimit注解
                if (!method.isAnnotationPresent(AccessLimit.class)) {
                    return true;
                }
                // 获取注解内容信息
                AccessLimit accessLimit = method.getAnnotation(AccessLimit.class);
                if (accessLimit == null) {
                    return true;
                }
                int limitCount = accessLimit.count();// 请求次数
                int limitTime = accessLimit.period();// 请求时间范围
                String key = null;
                String requestIp = NetWorkUtils.getIpAddr(request);
                String requestURI = request.getRequestURI();
                StringBuffer defaultKeyBuf = new StringBuffer("limit_");
                LimitType limitType = accessLimit.limitType();
                switch (limitType) {
                case IP:
                    key = defaultKeyBuf.append(requestIp).toString();
                    break;
                case CUSTOMER:
                    // 如果此处想根据表达式或者一些规则生成
                    key = defaultKeyBuf.append(accessLimit.key()).toString();
                    break;
                case IP_API:
                    // 根据 IP + API 限流 (默认)
                    key = defaultKeyBuf.append(requestIp).append("_").append(requestURI).toString();
                    break;
                default:
                    key = defaultKeyBuf.append(StringUtils.upperCase(method.getName())).toString();
                }
                
                //新的限流方式,先对个别接口使用
                Bucket bucket = LOCAL_CACHE.computeIfAbsent(key, k -> createNewBucket());
                logger.info("common api={} ,令牌通可用的Token数量={} ", key, bucket.getAvailableTokens());
                if (bucket.tryConsume(1)) {
                    return true;
                } else {
                    // 请求过于频繁
                    resultVo = new ResultVo(ResponseStateCode.API_REQUEST_TOO_MUCH.getCode(),
                            ResponseStateCode.API_REQUEST_TOO_MUCH.getMsg());
                    SendMsgUtil.contructJsonResponse(response, resultVo);
                    BaseBean baseBean = commonUtil.getBaseBeanFromRequest(request);
                    interfaceLogService.insertAsync(NetWorkUtils.getIpAddr(request), requestTime, request.getRequestURI(),
                            ((HandlerMethod) handler).getBean().getClass().getName(), baseBean, resultVo);
                    return false;
                }
            }
        } catch (Exception e) {
            logger.error("AccessLimitInterceptor error", e);
            SendMsgUtil.contructJsonResponse(response, resultVo);
            BaseBean baseBean = commonUtil.getBaseBeanFromRequest(request);
            interfaceLogService.insertAsync(NetWorkUtils.getIpAddr(request), requestTime, request.getRequestURI(),
                    null, baseBean, resultVo);
            return false;
        }
        return true;
    }

    
    private Bucket createNewBucket() {
        Refill refill = Refill.of(refillTokens, refillDuration);
        Bandwidth limit = Bandwidth.classic(capacity, refill);
        return Bucket4j.builder().addLimit(limit).build();
    }

    public static Map<String, Bucket> getLocalCache() {
        return LOCAL_CACHE;
    }

    public int getCapacity() {
        return capacity;
    }

    public void setCapacity(int capacity) {
        this.capacity = capacity;
    }

    public int getRefillTokens() {
        return refillTokens;
    }

    public void setRefillTokens(int refillTokens) {
        this.refillTokens = refillTokens;
    }

    public Duration getRefillDuration() {
        return refillDuration;
    }

    public void setRefillDuration(Duration refillDuration) {
        this.refillDuration = refillDuration;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值