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 的保护越发重要,建议大家在设计系统时考虑适当的限流策略。希望本文能够为每位开发者提供有价值的参考,感谢阅读!