我正在开发一个Spring Boot Rest API来处理大量的传入请求调用。我的控制器如下所示:
@RestController
public class ApiController {
List<ApiObject> apiDataList;
@RequestMapping(value="/data",produces={MediaType.APPLICATION_JSON_VALUE},method=RequestMethod.GET)
public ResponseEntity<List<ApiObject>> getData(){
List<ApiObject> apiDataList=getApiData();
return new ResponseEntity<List<ApiObject>>(apiDataList,HttpStatus.OK);
}
@ResponseBody
@Async
public List<ApiObject> getApiData(){
List<ApiObject> apiDataList3=new List<ApiObject> ();
//do the processing
return apiDataList3;
}
}
所以现在我想为每个用户设置一个ratelimit。假设每个用户每分钟只能请求5个请求或类似的东西。如何设置每个用户的速率限制,每分钟只能进行5次api呼叫,如果用户请求的次数超过了我可以发回429响应?我们需要他们的IP地址吗?
感谢任何帮助。
答案 0 :(得分:11)
您在Spring中没有该组件。
答案 1 :(得分:7)
Spring没有开箱即用的速率限制。
有一个bucket4j-spring-boot-starter项目,该项目使用带有令牌桶算法的bucket4j库来限制对REST api的访问速率。您可以通过应用程序属性文件进行配置。 limit the access based on IP address or username有一个选项。
以简单设置为例,该设置独立于用户,在10秒内最多允许5个请求:
bucket4j:
enabled: true
filters:
- cache-name: buckets
url: .*
rate-limits:
- bandwidths:
- capacity: 5
time: 10
unit: seconds
如果您使用的是Netflix Zuul,则可以使用Spring Cloud Zuul RateLimit,它使用不同的存储选项:Consul,Redis,Spring Data和Bucket4j。
答案 2 :(得分:0)
对于那些寻求限制每个用户(IP地址)每秒请求的用户来说,这是一个解决方案。此解决方案需要Google的Guava library
。您将使用LoadingCache
类来存储请求计数和客户端ip地址。您还将需要javax.servlet-api
依赖性,因为您想在发生请求计数的地方使用servlet filter
。代码如下:
import javax.servlet.Filter;
@Component
public class requestThrottleFilter implements Filter {
private int MAX_REQUESTS_PER_SECOND = 5; //or whatever you want it to be
private LoadingCache<String, Integer> requestCountsPerIpAddress;
public requestThrottleFilter(){
super();
requestCountsPerIpAddress = CacheBuilder.newBuilder().
expireAfterWrite(1, TimeUnit.SECONDS).build(new CacheLoader<String, Integer>() {
public Integer load(String key) {
return 0;
}
});
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
throws IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse;
String clientIpAddress = getClientIP((HttpServletRequest) servletRequest);
if(isMaximumRequestsPerSecondExceeded(clientIpAddress))
httpServletResponse.sendError(429,"Too many requests");
filterChain.doFilter(servletRequest, servletResponse);
}
private boolean isMaximumRequestsPerSecondExceeded(String clientIpAddress){
int requests = 0;
try {
requests = requestCountsPerIpAddress.get(clientIpAddress);
if(requests > MAX_REQUESTS_PER_SECOND)
return true;
} catch (ExecutionException e) {
requests = 0;
}
requests++;
requestCountsPerIpAddress.put(clientIpAddress, requests);
return false;
}
public String getClientIP(HttpServletRequest request) {
String xfHeader = request.getHeader("X-Forwarded-For");
if (xfHeader == null){
return request.getRemoteAddr();
}
return xfHeader.split(",")[0]; // voor als ie achter een proxy zit
}
@Override
public void destroy() {
}
}
因此,这基本上是将所有发出请求的IP地址存储在LoadingCache
中。这就像一张特殊的地图,其中每个条目都有一个过期时间。在构造函数中,到期时间设置为1秒。这意味着在第一个请求中,一个IP地址及其请求计数仅在LoadingCache中存储一秒钟。到期后会自动从地图上删除。如果在那一秒内,更多的请求来自该IP地址,那么isMaximumRequestsPerSecondExceeded(String clientIpAddress)
会将这些请求添加到请求总数中,但是在此之前,请检查是否已超过每秒的最大请求数量。如果是这样,它将返回true,并且过滤器将返回状态码429的错误响应,表示请求过多。
这样,每位用户每秒只能发出一定数量的请求。
编辑:确保让Spring在保存过滤器的程序包上进行组件扫描,否则过滤器将无法工作。另外,由于该过滤器带有@Component注释,因此默认情况下(/ *),该过滤器将适用于所有端点。
如果spring检测到您的过滤器,则在启动过程中您应该会在日志中看到类似的内容。
o.s.b.w.servlet.FilterRegistrationBean : Mapping filter:'requestThrottleFilter' to: [/*]