当前位置: 代码网 > it编程>编程语言>Java > SpringBoot中基于AOP和Semaphore实现API限流

SpringBoot中基于AOP和Semaphore实现API限流

2024年10月20日 Java 我要评论
为了在 spring boot 中使用 aop 实现速率限制:定义自定义注释来标记应该限速的方法。创建一个方面类,拦截用自定义注释注释的方法调用。使用速率限制器组件来跟踪和执行速率限制。处理速率限制超

为了在 spring boot 中使用 aop 实现速率限制:

  • 定义自定义注释来标记应该限速的方法。
  • 创建一个方面类,拦截用自定义注释注释的方法调用。
  • 使用速率限制器组件来跟踪和执行速率限制。
  • 处理速率限制超出的情况,如通过抛出自定义异常。

spring boot api 中的速率限制

可以使用各种技术在 spring boot api 中实现速率限制。一种常见的方法是使用 spring aop来拦截传入的请求并实施速率限制。

步骤 1 - 定义速率限制配置

创建一个配置类,在其中定义速率限制参数,例如允许的请求数和时间段。

@configuration
public class ratelimitconfig {
    @value("${rate.limit.requests}")
    private int requests;

    @value("${rate.limit.seconds}")
    private int seconds;

    // getters and setters
}

步骤 2 - 创建速率限制方面

使用 spring aop 实现一个方面来拦截方法调用并强制执行速率限制。

@aspect
@component
public class ratelimitaspect {
    @autowired
    private ratelimitconfig ratelimitconfig;

    @autowired
    private ratelimiter ratelimiter;

    @around("@annotation(ratelimited)")
    public object enforceratelimit(proceedingjoinpoint joinpoint) throws throwable {
        string key = getkey(joinpoint);
        if (!ratelimiter.tryacquire(key, ratelimitconfig.getrequests(), ratelimitconfig.getseconds())) {
            throw new ratelimitexceededexception("rate limit exceeded");
        }
        return joinpoint.proceed();
    }

    private string getkey(proceedingjoinpoint joinpoint) {
        //为正在调用的方法生成唯一密钥
        //方法签名、用户id、ip地址等。

    }
}

步骤 3 — 定义 ratelimited 注释

创建自定义注释来标记应受速率限制的方法。

@target(elementtype.method)
@retention(retentionpolicy.runtime)
  public @interface ratelimited {
}

步骤 4 - 实施速率限制器

创建速率限制器组件,使用令牌桶算法或任何其他合适的算法来管理速率限制。

@component
public class ratelimiter {
    private final map<string,ratelimitedsemaphore> semaphores = new concurrenthashmap<>();

    public boolean tryacquire(string key, int requests, int seconds) {
        
        long currenttime = system.currenttimemillis();

        // 计算时间窗口
        long starttime = currenttime - seconds * 1000;

        // 过期删除
        cleanupexpiredentries(starttime);

        // 获取semaphore 
        ratelimitedsemaphore semaphore = semaphores.computeifabsent(key, k -> {
            ratelimitedsemaphore newsemaphore = new ratelimitedsemaphore(requests);
            newsemaphore.setlastacquiretime(currenttime); // set last acquire time
            return newsemaphore;
        });

        // 校验 semaphore 
        boolean acquired = semaphore.tryacquire();
        if (acquired) {
            semaphore.setlastacquiretime(currenttime); 
            // 更新
        }
        return acquired;
    }

    private void cleanupexpiredentries(long starttime) {
        iterator<map.entry<string, ratelimitedsemaphore>> iterator = semaphores.entryset().iterator();
        while (iterator.hasnext()) {
            map.entry<string, ratelimitedsemaphore> entry = iterator.next();
            string key = entry.getkey();
            ratelimitedsemaphore semaphore = entry.getvalue();
            if (semaphore.getlastacquiretime() < starttime) {
                iterator.remove();
            }
        }
    }

    private class ratelimitedsemaphore extends semaphore {
        private volatile long lastacquiretime;

        public ratelimitedsemaphore(int permits) {
            super(permits);
        }

        public long getlastacquiretime() {
            return lastacquiretime;
        }

        public void setlastacquiretime(long lastacquiretime) {
            this.lastacquiretime = lastacquiretime;
        }
    }
}

步骤 5 - 注释控制器方法

用注解来注释应该进行速率限制的控制器方法 @ratelimited。

@restcontroller
public class mycontroller {
    @ratelimited
    @getmapping("/api/resource")
    public responseentity<string> getresource() {
        // implementation
    }
}

步骤 6 - 配置速率限制属性

application.properties在您的 或 中配置速率限制属性 application.yml。

rate.limit.requests=10
rate.limit.seconds=60

要按 ip 地址限制请求,可以从传入请求中提取 ip 地址并将其用作速率限制的密钥:

private string getkey(httpservletrequest request) {
    string ipaddress = request.getremoteaddr();
    return ipaddress; //用id做key
}

还需要修改enforceratelimit 中的方法 ratelimitaspect 以将对象传递 httpservletrequest 给 getkey 方法:

@around("@annotation(ratelimited)")
public object enforceratelimit(proceedingjoinpoint joinpoint) throws throwable {
   
    servletrequestattributes requestattributes = (servletrequestattributes) requestcontextholder.getrequestattributes();
    httpservletrequest request = requestattributes.getrequest();

    string key = getkey(request);
    if (!ratelimiter.tryacquire(key, ratelimitconfig.getrequests(), ratelimitconfig.getseconds())) {
        throw new ratelimitexceededexception("rate limit exceeded");
    }
    return joinpoint.proceed();
}

以上就是springboot中基于aop和semaphore实现api限流的详细内容,更多关于springboot实现api限流的资料请关注代码网其它相关文章!

(0)

相关文章:

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

发表评论

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