根据对方IP地址来限制接口的QPS(每秒查询率),你可以结合Spring Boot应用、Guava的RateLimiter或者自定义的并发控制逻辑来实现。以下是一个基于Guava RateLimiter和Spring Boot的示例,展示如何根据IP地址来限制接口的QPS:
- 添加Guava依赖
首先,确保你的pom.xml
文件中包含了Guava的依赖。
- 创建RateLimiter存储
你可以使用ConcurrentHashMap
来存储每个IP地址对应的RateLimiter。
java
import com.google.common.util.concurrent.RateLimiter;
import java.util.concurrent.ConcurrentHashMap;
public class IpRateLimiter {
private final ConcurrentHashMap<String, RateLimiter> ipRateLimiterMap = new ConcurrentHashMap<>();
private final double rate; // 例如:每秒允许的请求数
public IpRateLimiter(double rate) {
this.rate = rate;
}
public synchronized RateLimiter getRateLimiter(String ip) {
return ipRateLimiterMap.computeIfAbsent(ip, k -> RateLimiter.create(rate));
}
}
- 在Controller中使用RateLimiter
在Controller中,你可以从请求中获取IP地址,并使用IpRateLimiter
来获取对应的RateLimiter,然后检查是否允许请求通过。
java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
@RestController
public class MyController {
@Autowired
private IpRateLimiter ipRateLimiter;
@GetMapping("/limited-endpoint")
public String limitedEndpoint(HttpServletRequest request) {
String ipAddress = request.getRemoteAddr(); // 简化的IP获取方式,可能需要根据实际情况调整
RateLimiter rateLimiter = ipRateLimiter.getRateLimiter(ipAddress);
if (!rateLimiter.tryAcquire()) {
// 如果无法获取许可,则返回错误信息或执行其他逻辑
return "Too Many Requests from IP: " + ipAddress + ", please try again later.";
}
// 如果可以获取许可,则处理请求并返回结果
return "Success";
}
}
注意 :上面的request.getRemoteAddr()
方法可能只返回内网IP(如果请求经过反向代理),你可能需要配置X-Forwarded-For头来获取真实的客户端IP。
- 配置X-Forwarded-For
如果你的应用部署在反向代理(如Nginx)后面,你需要确保反向代理正确地设置了X-Forwarded-For头,并且你的Spring Boot应用能够解析这个头来获取真实的客户端IP。
- 清理RateLimiter
由于RateLimiter实例在ipRateLimiterMap
中会一直存在,直到应用重启,你可能需要实现一种机制来清理长时间没有活动的RateLimiter实例,以避免内存泄漏。这可以通过定期扫描并移除长时间未使用的RateLimiter来实现。
- 其他考虑
- 如果有大量不同的IP地址访问你的接口,
ipRateLimiterMap
可能会变得非常大。你可能需要考虑如何限制其大小或实现一种缓存淘汰策略。 - 在生产环境中,你可能还需要考虑如何优雅地处理被限流的请求,例如返回特定的HTTP状态码、记录日志等。