在若依框架中实现API接口限流策略,可以通过多种方式来完成,例如使用Spring Cloud Gateway自带的限流功能、基于Redis的分布式限流、或者通过自定义注解和AOP实现。本文将详细解析如何基于Redis实现API接口限流,并扩展讨论限流策略的设计与优化。
限流(Rate Limiting)是一种保护系统免受流量冲击的重要机制,主要目的是防止因请求量过大而导致系统崩溃或性能下降。常见的限流算法包括:
在实际应用中,令牌桶算法因其灵活性和高效性被广泛采用。
若依框架基于Spring Boot构建,因此需要引入Redis相关依赖。在pom.xml
中添加以下内容:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
同时确保Redis服务已正确配置并启动。
在application.yml
中配置Redis连接信息:
spring:
redis:
host: localhost
port: 6379
password:
timeout: 5000ms
lettuce:
pool:
max-active: 8
max-idle: 8
min-idle: 0
使用Redis的INCR
命令实现计数器限流:
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;
@Component
public class RateLimiter {
@Resource
private StringRedisTemplate redisTemplate;
/**
* 判断是否允许访问
*
* @param key 缓存key,通常为接口路径+IP地址
* @param limit 最大访问次数
* @param timeWindow 时间窗口(秒)
* @return true:允许访问;false:拒绝访问
*/
public boolean isAllowed(String key, int limit, long timeWindow) {
Long count = redisTemplate.opsForValue().increment(key, 1);
if (count == 1) {
// 第一次访问,设置过期时间
redisTemplate.expire(key, timeWindow, TimeUnit.SECONDS);
}
return count <= limit;
}
}
通过自定义注解标记需要限流的接口:
import java.lang.annotation.*;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ApiLimit {
int limit() default 100; // 默认最大访问次数
long timeWindow() default 60; // 默认时间窗口(秒)
}
使用AOP对标注了@ApiLimit
的接口进行拦截:
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 javax.annotation.Resource;
import java.lang.reflect.Method;
@Aspect
@Component
public class ApiLimitAspect {
@Resource
private RateLimiter rateLimiter;
@Around("@annotation(apiLimit)")
public Object handleApiLimit(ProceedingJoinPoint joinPoint, ApiLimit apiLimit) throws Throwable {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
// 构造缓存key
String ip = getRemoteIp(); // 获取客户端IP
String methodName = method.getName();
String key = "api_limit:" + methodName + ":" + ip;
// 判断是否允许访问
if (!rateLimiter.isAllowed(key, apiLimit.limit(), apiLimit.timeWindow())) {
throw new RuntimeException("访问过于频繁,请稍后再试!");
}
return joinPoint.proceed();
}
private String getRemoteIp() {
// 模拟获取客户端IP,实际项目中需从HttpServletRequest中获取
return "127.0.0.1";
}
}
在Controller中添加一个测试接口:
@RestController
@RequestMapping("/test")
public class TestController {
@GetMapping("/hello")
@ApiLimit(limit = 5, timeWindow = 10)
public String hello() {
return "Hello, World!";
}
}
启动项目后,连续访问/test/hello
接口,超过5次(10秒内)将返回错误提示。
以下是限流逻辑的流程图:
sequenceDiagram participant Client as 客户端 participant Controller as 控制器 participant Redis as Redis participant RateLimiter as 限流器 Client->>Controller: 发起请求 Controller->>RateLimiter: 调用isAllowed方法 RateLimiter->>Redis: INCR操作 Redis-->>RateLimiter: 返回计数值 alt 计数值小于限值 RateLimiter-->>Controller: 允许访问 Controller-->>Client: 返回响应数据 else 计数值超出限值 RateLimiter-->>Controller: 拒绝访问 Controller-->>Client: 返回错误提示 end