书接前文,上篇文章说明了限制访问的原理,这篇文章继续讲解下实现代码。
一、实际代码示例
1.基于拦截器实现Rate Limiting
首先,我们需要创建一个自定义的拦截器类,并在其中实现限流逻辑。以下是一个示例代码:
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
public class RateLimitingInterceptor implements HandlerInterceptor {
private final ConcurrentMap<String, Long> requestCounts = new ConcurrentHashMap<>();
private static final long ALLOWED_REQUESTS_PER_MINUTE = 60;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String clientIp = request.getRemoteAddr();
long currentTimeMillis = System.currentTimeMillis();
requestCounts.putIfAbsent(clientIp, currentTimeMillis);
long lastRequestTime = requestCounts.get(clientIp);
if (TimeUnit.MILLISECONDS.toMinutes(currentTimeMillis - lastRequestTime) < 1) {
long requestCount = requestCounts.values().stream().filter(time -> TimeUnit.MILLISECONDS.toMinutes(currentTimeMillis - time) < 1).count();
if (requestCount > ALLOWED_REQUESTS_PER_MINUTE) {
response.setStatus(HttpServletResponse.SC_TOO_MANY_REQUESTS);
response.getWriter().write("Too many requests");
return false;
}
}
requestCounts.put(clientIp, currentTimeMillis);
return true;
}
}
// 然后,在Spring Boot应用的配置类中注册这个拦截器:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private RateLimitingInterceptor rateLimitingInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(rateLimitingInterceptor).addPathPatterns("/api/**");
}
}
2.使用Bucket4j实现Rate Limiting
首先,在项目中引入Bucket4j依赖:
<dependency>
<groupId>com.github.vladimir-bukhtoyarov</groupId>
<artifactId>bucket4j-core</artifactId>
<version>7.0.0</version>
</dependency>
然后,创建一个自定义的过滤器类,并在其中实现限流逻辑:
import io.github.bucket4j.Bandwidth;
import io.github.bucket4j.Bucket;
import io.github.bucket4j.Bucket4j;
import io.github.bucket4j.Refill;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.time.Duration;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
public class Bucket4jRateLimitingFilter implements Filter {
private final ConcurrentMap<String, Bucket> buckets = new ConcurrentHashMap<>();
@
Override
public void init(FilterConfig filterConfig) throws ServletException {
// 初始化过滤器
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
String clientIp = httpRequest.getRemoteAddr();
Bucket bucket = buckets.computeIfAbsent(clientIp, this::newBucket);
if (bucket.tryConsume(1)) {
chain.doFilter(request, response);
} else {
httpResponse.setStatus(HttpServletResponse.SC_TOO_MANY_REQUESTS);
httpResponse.getWriter().write("Too many requests");
}
}
private Bucket newBucket(String clientIp) {
return Bucket4j.builder()
.addLimit(Bandwidth.classic(60, Refill.greedy(60, Duration.ofMinutes(1))))
.build();
}
@Override
public void destroy() {
// 销毁过滤器
}
}
// 然后,在Spring Boot应用的配置类中注册这个过滤器:
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean<Bucket4jRateLimitingFilter> loggingFilter(){
FilterRegistrationBean<Bucket4jRateLimitingFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new Bucket4jRateLimitingFilter());
registrationBean.addUrlPatterns("/api/*");
return registrationBean;
}
}