本文目录导读:

Java接口限流的实现方式有很多种,这里我为你梳理几种常用的方案,并附上核心代码示例。
基于RateLimiter的令牌桶限流
使用Guava的RateLimiter实现,适合单机限流。
import com.google.common.util.concurrent.RateLimiter;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.PostConstruct;
@RestController
public class TokenBucketLimiterController {
// 每秒生成2个令牌,即QPS限制为2
private RateLimiter rateLimiter;
@PostConstruct
public void init() {
rateLimiter = RateLimiter.create(2.0);
}
@GetMapping("/api/limit")
public String limitApi() {
// 尝试获取令牌,等待超时时间为500ms
if (rateLimiter.tryAcquire(500, TimeUnit.MILLISECONDS)) {
return "请求成功,处理业务逻辑";
} else {
return "请求过于频繁,请稍后再试";
}
}
}
基于Spring AOP的注解限流
这种方式更灵活,可以自定义限流策略。
1 自定义限流注解
import java.lang.annotation.*;
import java.util.concurrent.TimeUnit;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RateLimit {
// 每秒允许的请求数
double limit() default 10.0;
// 获取令牌的超时时间
long timeout() default 500;
// 超时时间单位
TimeUnit timeUnit() default TimeUnit.MILLISECONDS;
// 限流key,支持SpEL表达式
String key() default "";
}
2 AOP切面实现
import com.google.common.util.concurrent.RateLimiter;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.util.concurrent.ConcurrentHashMap;
@Aspect
@Component
public class RateLimitAspect {
// 存储每个方法对应的RateLimiter
private final ConcurrentHashMap<String, RateLimiter> rateLimiterMap = new ConcurrentHashMap<>();
@Around("@annotation(com.example.annotation.RateLimit)")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
RateLimit rateLimit = method.getAnnotation(RateLimit.class);
if (rateLimit != null) {
// 生成限流key
String key = getKey(joinPoint, rateLimit);
RateLimiter limiter = rateLimiterMap.computeIfAbsent(key,
k -> RateLimiter.create(rateLimit.limit()));
// 尝试获取令牌
if (limiter.tryAcquire(rateLimit.timeout(), rateLimit.timeUnit())) {
return joinPoint.proceed();
} else {
throw new RuntimeException("请求过于频繁,请稍后再试");
}
}
return joinPoint.proceed();
}
private String getKey(ProceedingJoinPoint joinPoint, RateLimit rateLimit) {
StringBuilder key = new StringBuilder(joinPoint.getSignature().toString());
// 支持SpEL表达式的key扩展
if (!rateLimit.key().isEmpty()) {
key.append(":").append(rateLimit.key());
}
return key.toString();
}
}
3 使用示例
@RestController
public class UserController {
@RateLimit(limit = 5, key = "#userId")
@GetMapping("/user/{userId}")
public String getUserInfo(@PathVariable String userId) {
// 处理业务逻辑
return "用户信息";
}
@RateLimit(limit = 1.0) // 每秒1次
@PostMapping("/order/create")
public String createOrder() {
return "创建订单成功";
}
}
基于Redis的分布式限流
适用于分布式系统,使用Redis Lua脚本保证原子性。
1 Redis Lua限流脚本
-- 限流脚本
local key = KEYS[1] -- 限流key
local limit = tonumber(ARGV[1]) -- 最大请求数
local windowSize = tonumber(ARGV[2]) -- 窗口大小(秒)
local currentTime = tonumber(ARGV[3]) -- 当前时间戳
-- 移除过期的请求记录
redis.call('zremrangebyscore', key, 0, currentTime - windowSize * 1000)
-- 获取当前窗口内的请求数
local currentCount = redis.call('zcard', key)
if currentCount < limit then
-- 添加当前请求
redis.call('zadd', key, currentTime, currentTime .. math.random())
redis.call('expire', key, windowSize)
return 1 -- 允许通过
else
return 0 -- 限流
end
2 分布式限流实现
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.PostConstruct;
import java.util.Arrays;
import java.util.List;
@Component
public class RedisRateLimiter {
@Autowired
private StringRedisTemplate redisTemplate;
private DefaultRedisScript<Long> rateLimitScript;
@PostConstruct
public void init() {
rateLimitScript = new DefaultRedisScript<>();
rateLimitScript.setScriptText(
"local key = KEYS[1]\n" +
"local limit = tonumber(ARGV[1])\n" +
"local windowSize = tonumber(ARGV[2])\n" +
"local currentTime = tonumber(ARGV[3])\n\n" +
"redis.call('zremrangebyscore', key, 0, currentTime - windowSize * 1000)\n\n" +
"local currentCount = redis.call('zcard', key)\n\n" +
"if currentCount < limit then\n" +
" redis.call('zadd', key, currentTime, currentTime .. math.random())\n" +
" redis.call('expire', key, windowSize)\n" +
" return 1\n" +
"else\n" +
" return 0\n" +
"end"
);
rateLimitScript.setResultType(Long.class);
}
public boolean tryAcquire(String key, int limit, int windowSize) {
List<String> keys = Arrays.asList(key);
long currentTime = System.currentTimeMillis();
Long result = redisTemplate.execute(
rateLimitScript,
keys,
String.valueOf(limit),
String.valueOf(windowSize),
String.valueOf(currentTime)
);
return result != null && result == 1;
}
}
@RestController
public class RedisLimitController {
@Autowired
private RedisRateLimiter rateLimiter;
@GetMapping("/api/redis-limit")
public String redisLimitApi() {
String userId = "user_123";
String key = "rate_limit:" + userId;
boolean allowed = rateLimiter.tryAcquire(key, 5, 1); // 每秒5次
if (allowed) {
return "分布式限流-请求成功";
} else {
return "请求过于频繁,请稍后再试";
}
}
}
基于拦截器的限流实现
通过Spring拦截器实现,适合全局限流。
import com.google.common.util.concurrent.RateLimiter;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.concurrent.ConcurrentHashMap;
@Component
public class RateLimitInterceptor implements HandlerInterceptor {
// 存储不同API的限流器
private final ConcurrentHashMap<String, RateLimiter> limiters = new ConcurrentHashMap<>();
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
String requestURI = request.getRequestURI();
// 根据不同接口配置不同限流策略
RateLimiter limiter = limiters.computeIfAbsent(requestURI, uri -> {
if (uri.contains("/api/important")) {
return RateLimiter.create(1.0); // 重要接口每秒1次
} else if (uri.contains("/api/common")) {
return RateLimiter.create(10.0); // 普通接口每秒10次
} else {
return RateLimiter.create(5.0); // 默认每秒5次
}
});
if (limiter.tryAcquire()) {
return true;
} else {
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write("{\"code\":429,\"message\":\"请求过于频繁\"}");
return false;
}
}
}
最佳实践建议
-
选择适合的方案
- 单机应用:使用RateLimiter
- 分布式系统:使用Redis限流
- 需要灵活配置:使用注解方式
-
限流粒度
- 按接口限流
- 按用户限流
- 按IP限流
-
限流策略组合
- 可以组合使用多个限流策略
- 全局QPS限制 + 单个用户限制
-
降级处理
- 限流后提供友好的提示信息
- 可以使用降级服务或缓存数据
-
监控告警
- 记录限流日志
- 监控限流触发次数
- 设置告警阈值
选择哪种方案取决于你的具体业务场景和系统架构,建议先确定需求,再选择合适的实现方式。
版权声明:足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播