在微服务和高并发系统中,限流(Rate Limiting)是一种非常重要的技术手段,用于保护系统免受过载,确保服务的稳定性。限流可以控制请求的速率,防止单个客户端或恶意用户消耗过多的资源,从而影响其他用户。
常见的限流算法包括:
使用 Spring Boot 实现限流,可以通过以下几种方式:
下面我们分别介绍这些方法的实现。
首先,我们创建一个限流过滤器,通过 AtomicInteger
或 Semaphore
来控制请求速率。
java复制代码import org.springframework.stereotype.Component;
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.HttpServletResponse;
import java.io.IOException;
import java.util.concurrent.Semaphore;
@Component
public class RateLimitingFilter implements Filter {
private static final int MAX_REQUESTS_PER_SECOND = 10;
private Semaphore semaphore = new Semaphore(MAX_REQUESTS_PER_SECOND);
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
if (semaphore.tryAcquire()) {
try {
chain.doFilter(request, response);
} finally {
semaphore.release();
}
} else {
((HttpServletResponse) response).setStatus(HttpServletResponse.SC_TOO_MANY_REQUESTS);
}
}
@Override
public void destroy() {
}
}
在 Spring Boot 应用中,过滤器自动注册,只需要添加 @Component
注解即可。不过,你也可以手动配置:
java复制代码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<RateLimitingFilter> loggingFilter() {
FilterRegistrationBean<RateLimitingFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new RateLimitingFilter());
registrationBean.addUrlPatterns("/*");
return registrationBean;
}
}
在 pom.xml
中添加 Bucket4j 依赖:
xml复制代码<dependency>
<groupId>com.github.vladimir-bukhtoyarov</groupId>
<artifactId>bucket4j-core</artifactId>
<version>6.2.0</version>
</dependency>
java复制代码import com.github.bucket4j.Bandwidth;
import com.github.bucket4j.Bucket;
import com.github.bucket4j.Refill;
import org.springframework.stereotype.Component;
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.HttpServletResponse;
import java.io.IOException;
import java.time.Duration;
@Component
public class Bucket4jRateLimitingFilter implements Filter {
private static final int CAPACITY = 10;
private static final int REFILL_TOKENS = 10;
private static final Duration REFILL_DURATION = Duration.ofSeconds(1);
private final Bucket bucket;
public Bucket4jRateLimitingFilter() {
Bandwidth limit = Bandwidth.classic(CAPACITY, Refill.greedy(REFILL_TOKENS, REFILL_DURATION));
this.bucket = Bucket.builder().addLimit(limit).build();
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
if (bucket.tryConsume(1)) {
chain.doFilter(request, response);
} else {
((HttpServletResponse) response).setStatus(HttpServletResponse.SC_TOO_MANY_REQUESTS);
}
}
@Override
public void destroy() {
}
}
与方法一类似,配置过滤器即可:
java复制代码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("/*");
return registrationBean;
}
}
在 pom.xml
中添加 Redis 和 Redisson 依赖:
xml复制代码<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>3.15.6</version>
</dependency>
在 application.properties
中配置 Redis:
properties复制代码spring.redis.host=localhost
spring.redis.port=6379
java复制代码import org.redisson.api.RRateLimiter;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
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.HttpServletResponse;
import java.io.IOException;
@Component
public class RedisRateLimitingFilter implements Filter {
@Autowired
private RedissonClient redissonClient;
private RRateLimiter rateLimiter;
@Bean
public RRateLimiter rateLimiter() {
RRateLimiter rateLimiter = redissonClient.getRateLimiter("rateLimiter");
rateLimiter.trySetRate(RRateType.OVERALL, 10, 1, RateIntervalUnit.SECONDS);
return rateLimiter;
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
this.rateLimiter = rateLimiter();
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
if (rateLimiter.tryAcquire(1)) {
chain.doFilter(request, response);
} else {
((HttpServletResponse) response).setStatus(HttpServletResponse.SC_TOO_MANY_REQUESTS);
}
}
@Override
public void destroy() {
}
}
与之前的方法一样,配置过滤器:
java复制代码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<RedisRateLimitingFilter> loggingFilter() {
FilterRegistrationBean<RedisRateLimitingFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new RedisRateLimitingFilter());
registrationBean.addUrlPatterns("/*");
return registrationBean;
}
}
在本指南中,我们介绍了三种在 Spring Boot 中实现限流的方法:
每种方法都有其优缺点和适用场景,可以根据具体需求选择合适的方案。希望本文能帮助你在项目中实现限流功能,保障系统的稳定性和可靠性。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。