Java 限制接口每个 IP 执行时间次数的注解

在现代软件开发中,保护 API 接口免受滥用非常重要。为了有效地避免接口请求过于频繁而导致的系统资源耗尽,我们可以使用基于注解的方式来对请求进行限制。本文将介绍如何在 Java 中通过自定义注解,实现对每个 IP 执行时间次数的限制。

1. 注解的定义

首先,我们需要定义一个注解,用于标记那些需要限制请求次数的接口。我们可以将这个注解命名为 @RateLimit

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

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RateLimit {
    int limit() default 5; // 允许的请求次数
    int period() default 60; // 限制周期,单位:秒
}

这个注解有两个参数:limit 表示限制的请求次数,period 表示限制的时间周期(以秒为单位),默认为每60秒最多5次请求。

2. 过滤器的实现

接下来,我们需要实现一个过滤器,用于拦截请求并检查当前 IP 地址的请求次数是否超过限制。下面是一个简单的示例,使用 ServletFilter 来实现:

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

public class RateLimitFilter implements Filter {

    private static final ConcurrentHashMap<String, RequestCounter> requestCounts = new ConcurrentHashMap<>();

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        String ipAddress = httpRequest.getRemoteAddr();

        // 获取请求方法上 RateLimit 注解的信息
        RateLimit rateLimit = getRateLimitAnnotation(httpRequest);

        if (rateLimit != null) {
            int limit = rateLimit.limit();
            int period = rateLimit.period();

            RequestCounter counter = requestCounts.computeIfAbsent(ipAddress, k -> new RequestCounter());
            long currentTime = System.currentTimeMillis();

            // 判断时间是否重置
            if (currentTime > counter.getResetTime()) {
                counter.reset(currentTime + TimeUnit.SECONDS.toMillis(period));
                counter.setCount(1);
            } else {
                counter.increment();
                if (counter.getCount() > limit) {
                    response.getWriter().write("请求过于频繁,请稍后再试");
                    return;
                }
            }
        }

        chain.doFilter(request, response);
    }

    private RateLimit getRateLimitAnnotation(HttpServletRequest request) {
        // 获取当前请求的方法
        try {
            String method = request.getMethod();
            String uri = request.getRequestURI();
            // 这里需要自己实现获取方法的方式,可以利用反射
            // return method.getAnnotation(RateLimit.class); // 伪代码
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    class RequestCounter {
        private int count = 0;
        private long resetTime;

        public synchronized void increment() {
            count++;
        }

        public synchronized void setCount(int count) {
            this.count = count;
        }

        public synchronized int getCount() {
            return count;
        }

        public long getResetTime() {
            return resetTime;
        }

        public void reset(long resetTime) {
            this.resetTime = resetTime;
        }
    }
}

在这个过滤器中,我们使用了一个 RequestCounter 类来记录每个 IP 的请求次数和重置时间。通过对 ConcurrentHashMap 的操作,我们可以安全地为每个 IP 维护一个请求计数器。

3. 测试和使用

为了测试我们的限流机制,我们可以使用以下简单的控制器示例:

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class RateLimitController {

    @RateLimit(limit = 3, period = 10)
    @GetMapping("/test")
    public String testEndpoint() {
        return "接口请求成功!";
    }
}

在这个例子中,我们设置了 /test 接口在 10 秒内最多可以被同一个 IP 请求 3 次。

4. 甘特图

通过一个甘特图来展示实施的时间线:

gantt
    title Rate Limiting Implementation Timeline
    dateFormat  YYYY-MM-DD
    section Implementation
    Define RateLimit Annotation       :a1, 2023-10-01, 1d
    Implement RateLimitFilter       :a2, 2023-10-02, 3d
    Create Test Controller            :a3, 2023-10-05, 2d
    Testing and Documentation         :a4, 2023-10-07, 2d

结尾

通过以上步骤,我们成功实现了一种基于注解的限流机制,以保护我们的 API 接口不受过度请求的影响。这种方式在 Java 中不仅清晰易用,而且能够根据需求进行灵活的扩展。随着业务的发展,对 API 的保护越发重要,建议大家在设计系统时考虑适当的限流策略。希望本文能够为每位开发者提供有价值的参考,感谢阅读!