当前位置: 代码网 > it编程>编程语言>Java > Springboot项目接口限流实现方案

Springboot项目接口限流实现方案

2024年08月19日 Java 我要评论
系统限流要求系统总并发数限制,如设置1000,表示该系统接口每秒可以请求1000次自定义系统接口请求并发数,也可以不加限流设置,如设置100,表示每秒可以请求100次该接口指定接口ip请求并发数,如设

系统限流要求

  • 系统总并发数限制,如设置1000,表示该系统接口每秒可以请求1000次
  • 自定义系统接口请求并发数,也可以不加限流设置,如设置100,表示每秒可以请求100次该接口
  • 指定接口ip请求并发数,如设置1,表示每秒该ip可以请求1次该接口

实现思路

  • 每秒系统总并发数限流实现,可以使用拦截器或过滤器,来处理系统总并发数限流的实现
  • 自定义系统接口请求并发数和指定接口ip请求并发数的实现,可以使用自定义注解和切面,来处理自定义系统接口请求并发数的实现
  • 可以使用redisson rratelimiter组件实现具体限流逻辑
  • 自定义业务异常类,当请求数超出请求限制时,来打断业务

核心代码

1.接口限流注解

package com.ocean.angel.tool.annotation;

import java.lang.annotation.*;

/**
 * 接口限流注解
 */
@retention(retentionpolicy.runtime)
@target(elementtype.method)
public @interface apilimiting {

    // 接口请求限制数
    int apirequestlimit() default 200;

    // 接口请求ip限制数
    int apiiplimit() default 1;
}

2.接口限流切面

package com.ocean.angel.tool.aspect;

import com.ocean.angel.tool.annotation.apilimiting;
import com.ocean.angel.tool.constant.apilimitingtypeenum;
import com.ocean.angel.tool.constant.resultcode;
import com.ocean.angel.tool.dto.apilimitingdata;
import com.ocean.angel.tool.exception.businessexception;
import com.ocean.angel.tool.util.ratelimiterkeyutil;
import lombok.extern.slf4j.slf4j;
import org.aspectj.lang.joinpoint;
import org.aspectj.lang.annotation.aspect;
import org.aspectj.lang.annotation.before;
import org.aspectj.lang.annotation.pointcut;
import org.aspectj.lang.reflect.methodsignature;
import org.redisson.api.rratelimiter;
import org.redisson.api.rateintervalunit;
import org.redisson.api.ratetype;
import org.redisson.api.redissonclient;
import org.springframework.stereotype.component;
import javax.annotation.resource;
import java.lang.reflect.method;

/**
 * 接口限流切面
 */
@slf4j
@aspect
@component
public class apilimitingaspect {

    @resource
    private redissonclient redissonclient;

    @pointcut("@annotation(com.ocean.angel.tool.annotation.apilimiting)")
    public void apilimitingaspect() {}

    @before(value = "apilimitingaspect()")
    public void apilimiting(joinpoint joinpoint) {
        apilimitingdata apilimitingdata = getapilimitdata(joinpoint);
        ratelimiterhandler(redissonclient, apilimitingdata);
    }

    /**
     * api 限流逻辑处理
     */
    private void ratelimiterhandler(redissonclient redissonclient, apilimitingdata apilimitingdata) {

        if(apilimitingdata.getapiiplimit() > 0) {

            // 获取rratelimiter实例
            rratelimiter ratelimiter = redissonclient.getratelimiter(getratelimiterkey(apilimitingdata, apilimitingtypeenum.api_ip_limit));

            // rratelimiter初始化
            if(!ratelimiterkeyutil.contains(getratelimiterkey(apilimitingdata, apilimitingtypeenum.api_ip_limit))) {
                ratelimiter.trysetrate(ratetype.overall, apilimitingdata.getapiiplimit(), 1, rateintervalunit.seconds);
            }

            // 超出接口请求ip限流设置,打断业务
            if (!ratelimiter.tryacquire()) {
                log.info("接口{}超出ip请求限制, 时间:{}",apilimitingdata.getmethodname(), system.currenttimemillis());
                throw new businessexception(resultcode.beyond_rate_limit);
            }
        }

        if(apilimitingdata.getapirequestlimit() > 0) {

            rratelimiter ratelimiter = redissonclient.getratelimiter(getratelimiterkey(apilimitingdata, apilimitingtypeenum.api_request_limit));

            if(!ratelimiterkeyutil.contains(getratelimiterkey(apilimitingdata, apilimitingtypeenum.api_request_limit))) {
                ratelimiter.trysetrate(ratetype.overall, apilimitingdata.getapirequestlimit(), 1, rateintervalunit.seconds);
            }

            // 超出接口请求限流设置,打断业务
            if (!ratelimiter.tryacquire()) {
                log.info("接口{}超出请求限制, 时间:{}",apilimitingdata.getmethodname(), system.currenttimemillis());
                throw new businessexception(resultcode.beyond_rate_limit);
            }
        }
    }

    /**
     * 组装apilimitingdata
     */
    private apilimitingdata getapilimitdata(joinpoint joinpoint) {

        apilimitingdata apilimitingdata = new apilimitingdata();

        methodsignature signature = (methodsignature) joinpoint.getsignature();
        method method = signature.getmethod();
        apilimitingdata.setmethodname(method.getname());

        apilimiting apilimiting = method.getannotation(apilimiting.class);
        apilimitingdata.setapirequestlimit(apilimiting.apirequestlimit());
        apilimitingdata.setapiiplimit(apilimiting.apiiplimit());

        return apilimitingdata;
    }

    /**
     * ratelimiter key
     */
    private string getratelimiterkey(apilimitingdata apilimitingdata, apilimitingtypeenum apilimitingtypeenum) {
        return apilimitingdata.getmethodname() + "_" + apilimitingtypeenum.getcode();
    }
}

3.系统接口限流拦截器

package com.ocean.angel.tool.interceptor;

import com.ocean.angel.tool.constant.resultcode;
import com.ocean.angel.tool.exception.businessexception;
import com.ocean.angel.tool.util.ratelimiterkeyutil;
import lombok.extern.slf4j.slf4j;
import org.redisson.api.rratelimiter;
import org.redisson.api.rateintervalunit;
import org.redisson.api.ratetype;
import org.redisson.api.redissonclient;
import org.springframework.web.servlet.handlerinterceptor;
import javax.servlet.http.httpservletrequest;
import javax.servlet.http.httpservletresponse;

@slf4j
public class apilimitinginterceptor implements handlerinterceptor {

    private final static string api_total_limit = "apitotallimit";

    // 系统每秒请求总数,30表示每秒最多处理30个请求
    private final static int api_total_limit_number = 30;
    private final redissonclient redissonclient;

    public apilimitinginterceptor(redissonclient redissonclient) {
        this.redissonclient = redissonclient;
    }

    @override
    public boolean prehandle(httpservletrequest request, httpservletresponse response, object handler) throws exception {

        rratelimiter ratelimiter = redissonclient.getratelimiter(api_total_limit);

        if(!ratelimiterkeyutil.contains(api_total_limit)) {
            ratelimiter.trysetrate(ratetype.overall, api_total_limit_number, 1, rateintervalunit.seconds);
        }

        // 超出系统接口总请求数限制,打断业务
        if (!ratelimiter.tryacquire()) {
            log.info("超出系统接口总请求数限制, 时间:{}", system.currenttimemillis());
            throw new businessexception(resultcode.beyond_rate_limit);
        }
        return true;
    }
}

4.接口自定义注解配置

@apilimiting(apirequestlimit = 5, apiiplimit = 1)
@getmapping("/limited/resource")
public resultbean<?> limitedresource() {
    return resultbean.success();
}

限流方案演示

下载源代码,github源码连接

修改application.yml和redission.yml,关于redis的相关配置

启动项目,调用http://localhost:8090/test/limited/resource接口,截图如下:

保持项目启动状态,运行com.ocean.angel.tool.applicationtests.contextloads()方法,截图如下:

使用指南

修改系统总请求数限制

调整系统接口限流参数

本文使用redisson rratelimiter组件实现具体限流逻辑,小伙伴们可以自己去手写具体限流功能(可以参考redission的限流相关的数据结构)

注意:

小伙伴们如果修改系统限流的配置,需要先删除redis里面的限流数据(如上图),不然修改不会生效。

本文使用以1秒为单位进行系统并发数控制,小伙伴可以根据需要自己去修改,如下:

ratelimiter.trysetrate(ratetype.overall, apilimitingdata.getapiiplimit(), 1, rateintervalunit.seconds)

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com