1. 首页 > Redis教程 > 正文

Redis教程FG033-Redis接口限流防护实战

本文档风哥主要介绍Redis接口限流防护实战,包括接口限流概念、接口限流实现原理、接口限流算法、接口限流使用场景、接口限流策略、接口限流配置、基于Redis的接口限流实现、接口限流使用示例、接口限流监控与告警以及实战案例等内容,风哥教程参考Redis官方文档等内容编写,适合DBA人员和开发人员在生产环境中使用。

Part01-基础概念与理论知识

1.1 接口限流概念

接口限流是指对API接口的访问频率进行限制,防止过多的请求导致系统过载,确保系统的稳定性和可用性。在分布式系统中,接口限流是一种重要的保护机制。

  • 目的:防止系统过载,确保系统的稳定性和可用性
  • 对象:API接口、用户、IP地址等
  • 方式:限制单位时间内的请求次数
  • 效果:保护系统,防止恶意攻击,提高系统的可靠性

1.2 接口限流实现原理

基于Redis的接口限流实现原理:

  • 计数器法:使用Redis的INCR命令统计单位时间内的请求次数
  • 滑动窗口:使用Redis的有序集合实现滑动窗口,统计窗口内的请求次数
  • 令牌桶:使用Redis实现令牌桶算法,控制请求的速率
  • 漏桶:使用Redis实现漏桶算法,平滑处理请求

1.3 接口限流算法

# 接口限流算法

## 1. 计数器法
– 原理:使用Redis的INCR命令统计单位时间内的请求次数,超过阈值则拒绝请求
– 优点:实现简单,性能高
– 缺点:可能出现突发流量,不够平滑

## 2. 滑动窗口
– 原理:使用Redis的有序集合实现滑动窗口,统计窗口内的请求次数
– 优点:更加平滑,避免突发流量
– 缺点:实现相对复杂

## 3. 令牌桶
– 原理:系统以恒定的速率生成令牌,请求需要获取令牌才能执行
– 优点:平滑处理请求,支持突发流量
– 缺点:实现相对复杂

## 4. 漏桶
– 原理:请求进入漏桶,漏桶以恒定的速率处理请求
– 优点:平滑处理请求,避免突发流量
– 缺点:无法处理突发流量

更多视频教程www.fgedu.net.cn

Part02-生产环境规划与建议

2.1 接口限流使用场景

接口限流使用场景:

  • API接口:限制API接口的访问频率,防止过载
  • 用户级别:限制单个用户的访问频率,防止恶意操作
  • IP级别:限制单个IP的访问频率,防止DDoS攻击
  • 资源访问:限制对特定资源的访问频率,如数据库、文件系统等
  • 第三方服务:限制对第三方服务的调用频率,避免超出配额

2.2 接口限流策略

# 接口限流策略

## 1. 基于时间窗口的限流
– 固定时间窗口:如每分钟限制100次请求
– 滑动时间窗口:如最近1分钟内限制100次请求

## 2. 基于用户的限流
– 每个用户每分钟限制100次请求
– 不同用户可以设置不同的限流阈值

## 3. 基于IP的限流
– 每个IP每分钟限制100次请求
– 可以根据IP的信誉度调整限流阈值

## 4. 基于接口的限流
– 不同接口设置不同的限流阈值
– 重要接口可以设置更严格的限流

## 5. 基于资源的限流
– 限制对数据库的查询频率
– 限制对文件系统的访问频率

2.3 接口限流配置

# 接口限流配置

## 1. 限流阈值设置
– 根据系统的处理能力设置限流阈值
– 根据业务需求设置合理的限流阈值
– 可以根据不同的接口、用户、IP设置不同的限流阈值

## 2. 限流时间窗口
– 固定时间窗口:如1分钟、5分钟、1小时
– 滑动时间窗口:如最近1分钟、最近5分钟

## 3. 限流策略配置
– 全局限流:对所有请求进行限流
– 局部限流:对特定接口、用户、IP进行限流
– 分级限流:根据不同的场景设置不同的限流策略

## 4. 限流处理方式
– 拒绝请求:直接返回错误信息
– 延迟处理:将请求放入队列,延迟处理
– 降级处理:返回缓存数据或默认数据

学习交流加群风哥QQ113257174

Part03-生产环境项目实施方案

3.1 基于Redis的接口限流实现

# 基于Redis的接口限流实现

## 1. 计数器法实现
public boolean isAllowed(String key, int limit, int window) {
String limitKey = “rate:limit:” + key;
Long current = redisTemplate.opsForValue().increment(limitKey);
if (current == 1) {
redisTemplate.expire(limitKey, window, TimeUnit.SECONDS);
}
return current <= limit; } ## 2. 滑动窗口实现 public boolean isAllowedWithSlidingWindow(String key, int limit, int window) { String windowKey = "rate:window:" + key; long currentTime = System.currentTimeMillis(); long windowStart = currentTime - window * 1000; // 移除窗口外的请求 redisTemplate.opsForZSet().removeRangeByScore(windowKey, 0, windowStart); // 统计窗口内的请求次数 Long count = redisTemplate.opsForZSet().zCard(windowKey); if (count >= limit) {
return false;
}

// 添加当前请求
redisTemplate.opsForZSet().add(windowKey, String.valueOf(currentTime), currentTime);

// 设置过期时间
redisTemplate.expire(windowKey, window, TimeUnit.SECONDS);
return true;
}

## 3. 令牌桶实现
public boolean isAllowedWithTokenBucket(String key, int rate, int capacity) {
String tokenKey = “rate:token:” + key;
String timestampKey = “rate:timestamp:” + key;

long currentTime = System.currentTimeMillis();
double tokensToAdd = (currentTime – redisTemplate.opsForValue().get(timestampKey) != null ? Long.parseLong(redisTemplate.opsForValue().get(timestampKey)) : 0) * rate / 1000.0;

double currentTokens = Math.min(capacity, redisTemplate.opsForValue().get(tokenKey) != null ? Double.parseDouble(redisTemplate.opsForValue().get(tokenKey)) : capacity);
currentTokens = Math.max(0, currentTokens + tokensToAdd);

if (currentTokens < 1) { return false; } currentTokens--; redisTemplate.opsForValue().set(tokenKey, String.valueOf(currentTokens)); redisTemplate.opsForValue().set(timestampKey, String.valueOf(currentTime)); redisTemplate.expire(tokenKey, 1, TimeUnit.HOURS); redisTemplate.expire(timestampKey, 1, TimeUnit.HOURS); return true; } ## 4. 限流工具类 public class RateLimitUtil { private static RedisTemplate redisTemplate;

public static void setRedisTemplate(RedisTemplate redisTemplate) {
RateLimitUtil.redisTemplate = redisTemplate;
}

public static boolean isAllowed(String key, int limit, int window) {
String limitKey = “rate:limit:” + key;
Long current = redisTemplate.opsForValue().increment(limitKey);
if (current == 1) {
redisTemplate.expire(limitKey, window, TimeUnit.SECONDS);
}
return current <= limit; } public static boolean isAllowedWithSlidingWindow(String key, int limit, int window) { String windowKey = "rate:window:" + key; long currentTime = System.currentTimeMillis(); long windowStart = currentTime - window * 1000; redisTemplate.opsForZSet().removeRangeByScore(windowKey, 0, windowStart); Long count = redisTemplate.opsForZSet().zCard(windowKey); if (count >= limit) {
return false;
}

redisTemplate.opsForZSet().add(windowKey, String.valueOf(currentTime), currentTime);
redisTemplate.expire(windowKey, window, TimeUnit.SECONDS);
return true;
}

public static boolean isAllowedWithTokenBucket(String key, int rate, int capacity) {
String tokenKey = “rate:token:” + key;
String timestampKey = “rate:timestamp:” + key;

long currentTime = System.currentTimeMillis();
double tokensToAdd = (currentTime – (redisTemplate.opsForValue().get(timestampKey) != null ? Long.parseLong(redisTemplate.opsForValue().get(timestampKey)) : 0)) * rate / 1000.0;

double currentTokens = Math.min(capacity, (redisTemplate.opsForValue().get(tokenKey) != null ? Double.parseDouble(redisTemplate.opsForValue().get(tokenKey)) : capacity));
currentTokens = Math.max(0, currentTokens + tokensToAdd);

if (currentTokens < 1) { return false; } currentTokens--; redisTemplate.opsForValue().set(tokenKey, String.valueOf(currentTokens)); redisTemplate.opsForValue().set(timestampKey, String.valueOf(currentTime)); redisTemplate.expire(tokenKey, 1, TimeUnit.HOURS); redisTemplate.expire(timestampKey, 1, TimeUnit.HOURS); return true; } }

3.2 接口限流使用示例

# 接口限流使用示例

## 1. API接口限流
@RestController
@RequestMapping(“/api”)
public class ApiController {
@Autowired
private RedisTemplate redisTemplate;

@PostConstruct
public void init() {
RateLimitUtil.setRedisTemplate(redisTemplate);
}

@GetMapping(“/resource”)
public ResponseEntity getResource(HttpServletRequest request) {
String key = “api:resource”;
boolean allowed = RateLimitUtil.isAllowed(key, 100, 60); // 每分钟限制100次请求
if (!allowed) {
return ResponseEntity.status(HttpStatus.TOO_MANY_REQUESTS).body(“Too many requests”);
}
return ResponseEntity.ok(“Resource accessed successfully”);
}
}

## 2. 用户级别限流
@RestController
@RequestMapping(“/user”)
public class UserController {
@Autowired
private RedisTemplate redisTemplate;

@PostConstruct
public void init() {
RateLimitUtil.setRedisTemplate(redisTemplate);
}

@GetMapping(“/profile”)
public ResponseEntity getUserProfile(@RequestParam String userId) {
String key = “user:” + userId;
boolean allowed = RateLimitUtil.isAllowed(key, 10, 60); // 每个用户每分钟限制10次请求
if (!allowed) {
return ResponseEntity.status(HttpStatus.TOO_MANY_REQUESTS).body(“Too many requests”);
}
return ResponseEntity.ok(“User profile accessed successfully”);
}
}

## 3. IP级别限流
@RestController
@RequestMapping(“/public”)
public class PublicController {
@Autowired
private RedisTemplate redisTemplate;

@PostConstruct
public void init() {
RateLimitUtil.setRedisTemplate(redisTemplate);
}

@GetMapping(“/info”)
public ResponseEntity getPublicInfo(HttpServletRequest request) {
String ip = request.getRemoteAddr();
String key = “ip:” + ip;
boolean allowed = RateLimitUtil.isAllowed(key, 50, 60); // 每个IP每分钟限制50次请求
if (!allowed) {
return ResponseEntity.status(HttpStatus.TOO_MANY_REQUESTS).body(“Too many requests”);
}
return ResponseEntity.ok(“Public info accessed successfully”);
}
}

## 4. 滑动窗口限流
@RestController
@RequestMapping(“/sliding”)
public class SlidingWindowController {
@Autowired
private RedisTemplate redisTemplate;

@PostConstruct
public void init() {
RateLimitUtil.setRedisTemplate(redisTemplate);
}

@GetMapping(“/resource”)
public ResponseEntity getResource() {
String key = “sliding:resource”;
boolean allowed = RateLimitUtil.isAllowedWithSlidingWindow(key, 100, 60); // 滑动窗口每分钟限制100次请求
if (!allowed) {
return ResponseEntity.status(HttpStatus.TOO_MANY_REQUESTS).body(“Too many requests”);
}
return ResponseEntity.ok(“Resource accessed successfully”);
}
}

## 5. 令牌桶限流
@RestController
@RequestMapping(“/token”)
public class TokenBucketController {
@Autowired
private RedisTemplate redisTemplate;

@PostConstruct
public void init() {
RateLimitUtil.setRedisTemplate(redisTemplate);
}

@GetMapping(“/resource”)
public ResponseEntity getResource() {
String key = “token:resource”;
boolean allowed = RateLimitUtil.isAllowedWithTokenBucket(key, 100, 200); // 速率100个/秒,容量200个
if (!allowed) {
return ResponseEntity.status(HttpStatus.TOO_MANY_REQUESTS).body(“Too many requests”);
}
return ResponseEntity.ok(“Resource accessed successfully”);
}
}

3.3 接口限流监控与告警

# 接口限流监控与告警

## 1. 监控限流情况
$ /redis/app/bin/redis-cli -h 192.168.1.100 -p 6379 -a fgedu@2026 keys rate:*
$ /redis/app/bin/redis-cli -h 192.168.1.100 -p 6379 -a fgedu@2026 get rate:limit:api:resource

## 2. 监控脚本
#!/bin/bash
# rate_limit_monitor.sh
# from:www.itpux.com.qq113257174.wx:itpux-com
# web: `http://www.fgedu.net.cn`

REDIS_CLI=”/redis/app/bin/redis-cli”
HOST=”192.168.1.100″
PORT=”6379″
PASSWORD=”fgedu@2026″

# 监控限流情况
RATE_KEYS=$($REDIS_CLI -h $HOST -p $PORT -a $PASSWORD keys rate:*)
echo “当前限流键数量: $(echo “$RATE_KEYS” | wc -l)”

# 查看具体限流情况
echo “$RATE_KEYS” | xargs -I {} $REDIS_CLI -h $HOST -p $PORT -a $PASSWORD get {}

## 3. 告警机制
# 设置告警规则
– 限流次数超过阈值的80%
– 限流拒绝率超过10%
– 特定接口的限流次数异常

## 4. 使用Prometheus监控
# 配置Prometheus监控Redis
– job_name: ‘redis’
static_configs:
– targets: [‘192.168.1.100:9121’]

# 配置Grafana面板,监控限流情况

风哥提示:Redis接口限流是保护系统的重要机制,合理的限流策略可以防止系统过载,确保系统的稳定性和可用性。在实际应用中,需要根据具体业务场景和数据特点,选择合适的限流算法和策略。

Part04-生产案例与实战讲解

4.1 API接口限流

# API接口限流

## 1. 场景描述
– 公开API接口,需要限制访问频率
– 防止恶意攻击和过载

## 2. 解决方案
– 使用Redis的计数器法实现API接口限流
– 设置合理的限流阈值和时间窗口
– 实现监控和告警机制

## 3. 实战操作
# API接口限流实现
@RestController
@RequestMapping(“/api”)
public class ApiController {
@Autowired
private RedisTemplate redisTemplate;

@PostConstruct
public void init() {
RateLimitUtil.setRedisTemplate(redisTemplate);
}

@GetMapping(“/products”)
public ResponseEntity> getProducts() {
String key = “api:products”;
boolean allowed = RateLimitUtil.isAllowed(key, 100, 60); // 每分钟限制100次请求
if (!allowed) {
return ResponseEntity.status(HttpStatus.TOO_MANY_REQUESTS).body(null);
}
// 业务逻辑
List products = getProductsFromDB();
return ResponseEntity.ok(products);
}

private List getProductsFromDB() {
// 模拟从数据库获取产品列表
List products = new ArrayList<>();
products.add(new Product(“2001”, “iPhone 14”, 6999));
products.add(new Product(“2002”, “iPhone 14 Pro”, 8999));
products.add(new Product(“2003”, “iPhone 14 Pro Max”, 9999));
return products;
}
}

## 4. 测试
$ ab -n 200 -c 50 http://localhost:8080/api/products

# 输出示例
Concurrency Level: 50
Time taken for tests: 1.234 seconds
Complete requests: 200
Failed requests: 100
(Connect: 0, Receive: 0, Length: 100, Exceptions: 0)
Total transferred: 123456 bytes
HTML transferred: 12345 bytes
Requests per second: 162.07 [#/sec] (mean)
Time per request: 308.50 [ms] (mean)
Time per request: 6.17 [ms] (mean, across all concurrent requests)
Transfer rate: 98.76 [Kbytes/sec] received

Connection Times (ms):
min mean[+/-sd] median max
Connect: 0 0 0.1 0 1
Processing: 50 308 56.7 300 400
Waiting: 50 308 56.7 300 400
Total: 50 308 56.7 300 400

# 验证限流
$ /redis/app/bin/redis-cli -h 192.168.1.100 -p 6379 -a fgedu@2026 get rate:limit:api:products

# 输出示例
“100”

4.2 用户级别限流

# 用户级别限流

## 1. 场景描述
– 用户登录接口,需要限制单个用户的登录频率
– 防止暴力破解密码

## 2. 解决方案
– 使用Redis的计数器法实现用户级别限流
– 设置合理的限流阈值和时间窗口
– 实现监控和告警机制

## 3. 实战操作
# 用户登录限流实现
@RestController
@RequestMapping(“/auth”)
public class AuthController {
@Autowired
private RedisTemplate redisTemplate;
@Autowired
private UserService userService;

@PostConstruct
public void init() {
RateLimitUtil.setRedisTemplate(redisTemplate);
}

@PostMapping(“/login”)
public ResponseEntity login(@RequestBody LoginRequest request) {
String key = “user:login:” + request.getUsername();
boolean allowed = RateLimitUtil.isAllowed(key, 5, 60); // 每个用户每分钟限制5次登录尝试
if (!allowed) {
return ResponseEntity.status(HttpStatus.TOO_MANY_REQUESTS).body(new LoginResponse(false, “Too many login attempts”));
}

// 验证用户名和密码
boolean success = userService.validateCredentials(request.getUsername(), request.getPassword());
if (success) {
return ResponseEntity.ok(new LoginResponse(true, “Login successful”));
} else {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(new LoginResponse(false, “Invalid credentials”));
}
}
}

## 4. 测试
# 正常登录
$ curl -X POST http://localhost:8080/auth/login -H “Content-Type: application/json” -d ‘{“username”:”fgedu01″,”password”:”password123″}’

# 输出示例
{“success”:true,”message”:”Login successful”}

# 多次登录尝试
$ for i in {1..10}; do curl -X POST http://localhost:8080/auth/login -H “Content-Type: application/json” -d ‘{“username”:”fgedu01″,”password”:”wrongpassword”}’; done

# 输出示例
{“success”:false,”message”:”Invalid credentials”}
{“success”:false,”message”:”Invalid credentials”}
{“success”:false,”message”:”Invalid credentials”}
{“success”:false,”message”:”Invalid credentials”}
{“success”:false,”message”:”Invalid credentials”}
{“success”:false,”message”:”Too many login attempts”}
{“success”:false,”message”:”Too many login attempts”}
{“success”:false,”message”:”Too many login attempts”}
{“success”:false,”message”:”Too many login attempts”}
{“success”:false,”message”:”Too many login attempts”}

# 验证限流
$ /redis/app/bin/redis-cli -h 192.168.1.100 -p 6379 -a fgedu@2026 get rate:limit:user:login:fgedu01

# 输出示例
“10”

4.3 IP级别限流

# IP级别限流

## 1. 场景描述
– 公开接口,需要限制单个IP的访问频率
– 防止DDoS攻击

## 2. 解决方案
– 使用Redis的计数器法实现IP级别限流
– 设置合理的限流阈值和时间窗口
– 实现监控和告警机制

## 3. 实战操作
# IP级别限流实现
@RestController
@RequestMapping(“/public”)
public class PublicController {
@Autowired
private RedisTemplate redisTemplate;

@PostConstruct
public void init() {
RateLimitUtil.setRedisTemplate(redisTemplate);
}

@GetMapping(“/health”)
public ResponseEntity health(HttpServletRequest request) {
String ip = request.getRemoteAddr();
String key = “ip:” + ip;
boolean allowed = RateLimitUtil.isAllowed(key, 100, 60); // 每个IP每分钟限制100次请求
if (!allowed) {
return ResponseEntity.status(HttpStatus.TOO_MANY_REQUESTS).body(“Too many requests”);
}
return ResponseEntity.ok(“OK”);
}
}

## 4. 测试
$ ab -n 200 -c 50 http://localhost:8080/public/health

# 输出示例
Concurrency Level: 50
Time taken for tests: 0.876 seconds
Complete requests: 200
Failed requests: 100
(Connect: 0, Receive: 0, Length: 100, Exceptions: 0)
Total transferred: 123456 bytes
HTML transferred: 12345 bytes
Requests per second: 228.31 [#/sec] (mean)
Time per request: 219.00 [ms] (mean)
Time per request: 4.38 [ms] (mean, across all concurrent requests)
Transfer rate: 138.76 [Kbytes/sec] received

Connection Times (ms):
min mean[+/-sd] median max
Connect: 0 0 0.1 0 1
Processing: 30 219 45.6 210 300
Waiting: 30 219 45.6 210 300
Total: 30 219 45.6 210 300

# 验证限流
$ /redis/app/bin/redis-cli -h 192.168.1.100 -p 6379 -a fgedu@2026 get rate:limit:ip:127.0.0.1

# 输出示例
“100”

更多学习教程公众号风哥教程itpux_com

Part05-风哥经验总结与分享

5.1 最佳实践

Redis接口限流防护实战最佳实践:

  • 选择合适的限流算法:根据业务场景选择合适的限流算法,学习交流加群风哥微信: itpux-com
  • 设置合理的限流阈值:根据系统的处理能力和业务需求设置合理的限流阈值
  • 实现多级限流:对API、用户、IP等不同维度进行限流
  • 监控和告警:建立完善的监控和告警机制,及时发现和处理异常情况
  • 降级处理:当限流触发时,提供合理的降级处理方案
  • 性能优化:优化Redis的使用,提高限流的性能
  • 容错设计:当Redis不可用时,使用备用方案
  • 持续优化:根据实际情况,持续优化限流策略

5.2 常见问题

常见问题及解决:

  • 限流阈值设置不合理:根据系统的处理能力和业务需求调整限流阈值
  • 限流算法选择不当:根据业务场景选择合适的限流算法
  • Redis性能问题:优化Redis的使用,提高限流的性能
  • 监控不到位:建立完善的监控和告警机制
  • 降级处理不当:提供合理的降级处理方案
  • 容错设计不足:当Redis不可用时,使用备用方案

5.3 优化技巧

# 优化技巧

## 1. 限流算法优化
– 根据业务场景选择合适的限流算法
– 对于突发流量,使用令牌桶算法
– 对于平滑流量,使用漏桶算法
– 对于简单场景,使用计数器法

## 2. 限流粒度优化
– 对不同的接口设置不同的限流阈值
– 对不同的用户设置不同的限流阈值
– 对不同的IP设置不同的限流阈值

## 3. 性能优化
– 使用Redis的Pipeline批量操作,减少网络往返时间
– 优化Redis配置,提高性能
– 合理使用连接池,减少连接建立的开销
– 使用Lua脚本,减少Redis的访问次数

## 4. 监控优化
– 建立完善的监控和告警机制
– 实时监控限流情况,及时发现异常
– 定期分析限流数据,优化限流策略

## 5. 容错优化
– 当Redis不可用时,使用本地缓存作为备用方案
– 实现降级处理,当限流触发时,提供合理的响应
– 建立熔断机制,当系统负载过高时,拒绝部分请求

通过本文档的学习,您应该掌握了Redis接口限流防护实战,能够在生产环境中实施有效的接口限流方案,保护系统的稳定性和可用性。在实际应用中,需要根据具体业务场景和数据特点,选择合适的限流算法和策略,确保系统的高效运行。

from Redis视频:www.itpux.com

本文由风哥教程整理发布,仅用于学习测试使用,转载注明出处:http://www.fgedu.net.cn/10327.html

联系我们

在线咨询:点击这里给我发消息

微信号:itpux-com

工作日:9:30-18:30,节假日休息