利用RateLimiter加上自定义Aop注解限制某个接口的访问频率

实际工作中有这种需求:
某个接口被不同的外部ip大量访问,且每个IP的访问频率很高。为了节约后端服务器资源,于是想通过
RateLimiter令牌桶+自定义Aop注解来限制同一ip在单位时间内只能访问固定的次数,超过这个次数的请求被拦截掉。具体实现如下:
1、自定义Aop注解

package com.fancetech.tools.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;


@Retention(RetentionPolicy.RUNTIME)
@Target({
   
   ElementType.METHOD})
public @interface RequestLimitAnnotate {
   
   


    /**
     * 请求次数限制
     */
    long requestLimitNum();


    /**
     * 限流的时间间隔,单位秒
     */
    long timeInterval();
}

2、注解实现类

@Order(3)
@Aspect
@Component
@Slf4j
public class RequestLimitAop extends CBaseController {
   
   


    /**
     * 每秒产生的令牌数
     */
    private double permitsPerSecond;


	@Pointcut("@annotation(com.fancetech.tools.annotation.RequestLimitAnnotate)")
    public void 
<think>好的,我现在需要帮助用户找到在Java程序中限制接口访问量的方法或实现方案。用户之前已经提供了一些相关的引用内容,我需要仔细分析这些引用,并结合我的知识来给出合适的回答。 首先,引用[1]提到了RateLimiter和令牌桶算法,这是Google Guava库中的一个工具,可以用来控制速率。用户给出的示例代码展示了如何使用RateLimiter限制每秒的执行次数。这应该是一个有效的方法,因为令牌桶算法允许突发流量,同时又能维持平均速率。 接下来,引用[2]和[4]都涉及到使用Spring框架的AOP(面向切面编程)来实现访问次数限制。这里的关键是结合注解和切面来拦截方法调用,统计访问次数。例如,通过自定义注解@RequestLimit,然后在切面中检查单位时间内的调用次数,如果超过限制则抛出异常。引用[4]中的代码片段显示使用了一个ConcurrentHashMap来存储每个IP地址的访问次数,并设置了过期时间,这样可以自动清理旧的记录,避免内存泄漏。 引用[5]提到了记录和显示日访问量和总访问量,虽然主要关注的是统计,但其中的思路也可以用于限制访问量。比如,使用ServletContext来存储计数,但这种方法可能不够精确,尤其是在分布式系统中,不过对于单机应用可能有效。 现在需要综合这些方法,考虑用户可能的场景。用户可能是在开发Web应用,需要限制某个接口访问频率,防止恶意攻击或过度使用。根据引用内容,主要的实现方案有: 1. **Guava RateLimiter(令牌桶算法)**:适用于单机限流,配置简单,支持突发流量。 2. **Spring AOP + 自定义注解**:适合在Spring项目中实现方法级别的限流,可以结合IP或用户标识进行细粒度控制。 3. **分布式限流(Redis计数器)**:当应用部署在多个实例时,需要借助Redis这样的分布式存储来同步计数,确保全局限制。 4. **Semaphore信号量**:控制并发线程数,防止资源过载。 需要注意的是,RateLimiter和Semaphore都是单机的解决方案,如果应用是分布式的,可能需要使用Redis等中间件来实现跨实例的限流。另外,AOP的方式可以非侵入式地应用到现有代码中,提升可维护性。 用户可能还需要了解如何选择这些方案,比如根据应用架构是单机还是分布式,或者是否需要支持突发流量。例如,令牌桶算法允许一定程度的突发,而漏桶算法则更平滑。此外,引用[4]中的代码使用ConcurrentHashMap和自定义的过期策略,这可能在高并发下存在性能问题,而使用Caffeine或Guava的Cache库可以更高效地处理过期条目。 最后,应该提醒用户考虑限流后的处理策略,比如直接拒绝、排队等待或返回降级内容。同时,异常处理和监控也是重要部分,确保系统稳定性和可观察性。</think>在Java程序中实现接口访问限制主要有以下五种方案,根据应用场景和实现复杂度可选择不同方案: ### 一、Guava RateLimiter(令牌桶算法) 使用Google Guava库提供的令牌桶算法实现单机限流,适合控制接口访问速率: ```java public class ApiLimiter { private static final RateLimiter limiter = RateLimiter.create(5); // 每秒5个令牌 public void processRequest() { if (limiter.tryAcquire()) { // 非阻塞获取令牌 // 执行业务逻辑 } else { throw new RuntimeException("接口访问超限"); } } } ``` 优势:支持突发流量,配置简单[^1] ### 二、Spring AOP + 自定义注解 通过AOP切面实现方法级细粒度控制: ```java @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface RequestLimit { int count() default 5; int time() default 60000; // 单位毫秒 } @Aspect @Component public class RequestLimitAspect { private ConcurrentHashMap<String, AtomicInteger> counter = new ConcurrentHashMap<>(); @Around("@annotation(limit)") public Object checkLimit(ProceedingJoinPoint pjp, RequestLimit limit) throws Throwable { String key = getClientIP(); // 获取客户端标识 AtomicInteger count = counter.computeIfAbsent(key, k -> new AtomicInteger(0)); if (count.incrementAndGet() > limit.count()) { throw new RequestLimitException("访问过于频繁"); } try { return pjp.proceed(); } finally { count.decrementAndGet(); } } } ``` 特点:支持方法级注解配置,可与IP/用户绑定[^2][^4] ### 三、分布式限流(Redis实现) 使用Redis+Lua脚本保证原子操作: ```java public boolean accessLimit(String key, int limit, int timeout) { Jedis jedis = jedisPool.getResource(); String luaScript = "local current = redis.call('incr',KEYS[1]) " + "if current == 1 then redis.call('expire',KEYS[1],ARGV[2]) end " + "return current <= tonumber(ARGV[1])"; Object result = jedis.eval(luaScript, 1, key, String.valueOf(limit), String.valueOf(timeout)); return (Long)result == 1; } ``` 适用场景:微服务架构下的分布式限流 ### 四、Semaphore信号量控制 使用JDK原生并发工具控制并发量: ```java public class SemaphoreLimiter { private final Semaphore semaphore = new Semaphore(10); // 允许10个并发 public void process() throws InterruptedException { semaphore.acquire(); try { // 执行业务逻辑 } finally { semaphore.release(); } } } ``` 特点:控制并发线程数,防止资源耗尽 ### 五、Servlet Filter全局控制 通过过滤器实现接口访问统计: ```java @WebFilter("/*") public class AccessFilter implements Filter { private AtomicInteger counter = new AtomicInteger(0); private static final int MAX_ACCESS = 1000; public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) { if (counter.incrementAndGet() > MAX_ACCESS) { ((HttpServletResponse)res).sendError(429); return; } chain.doFilter(req, res); counter.decrementAndGet(); } } ``` 特点:适合全局请求控制[^5] ### 方案选型建议表 | 方案 | 适用场景 | 性能 | 分布式支持 | |------|---------|------|-----------| | RateLimiter | 单机速率控制 | 高 | ❌ | | Spring AOP | 方法级细粒度控制 | 中 | ❌ | | Redis | 分布式系统 | 中 | ✔️ | | Semaphore | 资源并发控制 | 高 | ❌ | | Servlet Filter | 全局请求限制 | 中 | ❌ |
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值