# HTTP 限流功能指南 ## 概述 为了保护服务器性能和防止恶意请求,系统实现了基于设备号的请求限流功能。相同的设备号在指定时间间隔内只能发起一次请求。 --- ## 配置 ### httpConfig.json ```json { "enabled": true, "port": 8080, "host": "0.0.0.0", "cors": { "enabled": true, "allowOrigin": "*" }, "rateLimit": { "enabled": true, "interval": 5000 } } ``` | 参数 | 说明 | 默认值 | |------|------|--------| | `rateLimit.enabled` | 是否启用限流 | `true` | | `rateLimit.interval` | 限流时间间隔(毫秒) | `5000` (5秒) | ### 常见配置 #### 无限流限制 ```json { "rateLimit": { "enabled": false } } ``` #### 严格限流(10秒) ```json { "rateLimit": { "enabled": true, "interval": 10000 } } ``` #### 宽松限流(2秒) ```json { "rateLimit": { "enabled": true, "interval": 2000 } } ``` --- ## 工作原理 ### 限流流程 ``` 客户端请求设备 D001 ↓ 检查 D001 的最后请求时间 ↓ 是否超过 5 秒? ├─ 是 → 允许请求,返回 200/404 └─ 否 → 拒绝请求,返回 429 (Too Many Requests) ``` ### 关键特点 - ✅ **基于设备号** - 限流对象是设备号,不同设备独立计算 - ✅ **固定间隔** - 同一设备在指定间隔内只允许一次请求 - ✅ **自动重置** - 超过间隔时间后自动解除限制 - ✅ **独立计数** - 每个设备的请求次数独立记录 --- ## API 端点 ### 1. 获取指定设备数据(带限流) ``` GET /api/device/data?deviceNumber=D001 ``` **成功响应 (HTTP 200/404):** ```json { "code": 0, "message": "success", "data": { "deviceNumber": "D001", "data": {...} } } ``` **被限流响应 (HTTP 429):** ```json { "code": 429, "message": "请求过于频繁,请在 3245ms 后再试", "timestamp": "2025-12-05T10:30:45.234Z" } ``` ### 2. 获取限流统计 ``` GET /api/ratelimit/stats ``` **响应示例:** ```json { "code": 0, "message": "success", "data": { "enabled": true, "interval": 5000, "totalIdentifiers": 3, "records": { "D001": { "lastRequestTime": 1734000645123, "count": 5 }, "D002": { "lastRequestTime": 1734000640000, "count": 3 } } }, "timestamp": "2025-12-05T10:30:45.234Z" } ``` | 字段 | 说明 | |------|------| | `enabled` | 限流是否启用 | | `interval` | 限流间隔(毫秒) | | `totalIdentifiers` | 记录的设备总数 | | `records` | 详细的限流记录 | ### 3. 清空限流记录 ``` POST /api/ratelimit/clear ``` **响应示例:** ```json { "code": 0, "message": "success", "data": { "message": "限流记录已清空" }, "timestamp": "2025-12-05T10:30:45.234Z" } ``` --- ## 测试 ### 运行基础限流测试 ```bash node rateLimitTest.js ``` 这会执行 9 个测试用例,验证: 1. ✅ 获取限流配置 2. ✅ 清空限流记录 3. ✅ 首次请求成功 4. ✅ 立即重复请求被限流 5. ✅ 部分间隔后仍被限流 6. ✅ 完整间隔后请求成功 7. ✅ 不同设备限流独立 8. ✅ 获取限流统计 9. ✅ 清空后可立即请求 ### 运行压力测试 ```bash node rateLimitTest.js stress ``` 这会快速发送 100 个请求到同一设备,展示限流效果。 ### 测试输出示例 ``` 🚀 开始测试限流功能... ============================================================ 【测试 1】获取限流配置信息 ✅ API 文档获取成功 限流启用: true 限流间隔: 5000ms 【测试 3】首次请求设备 D001(应该成功) 状态: 404 ✅ 首次请求成功 (设备不存在返回 404,但不受限流限制) 【测试 4】立即再次请求设备 D001(应该被限流) 状态: 429 ✅ 请求被限流 (HTTP 429) 错误信息: 请求过于频繁,请在 4987ms 后再试 【测试 6】再等待 3 秒后请求设备 D001(总计 6s,应该成功) 状态: 404 ✅ 限流已解除,请求成功 ============================================================ ✅ 限流功能测试完成 ``` --- ## 使用示例 ### JavaScript/Node.js ```javascript const http = require('http'); function queryDevice(deviceNumber, retryInterval = 5000) { return new Promise((resolve, reject) => { const url = new URL(`http://localhost:8080/api/device/data?deviceNumber=${deviceNumber}`); const req = http.get(url, (res) => { let data = ''; res.on('data', chunk => data += chunk); res.on('end', () => { const result = JSON.parse(data); if (res.statusCode === 429) { // 被限流,解析剩余等待时间 const match = result.message.match(/(\d+)ms/); const remainingTime = match ? parseInt(match[1]) : retryInterval; console.log(`限流中,${remainingTime}ms 后重试...`); setTimeout(() => { queryDevice(deviceNumber, retryInterval) .then(resolve) .catch(reject); }, remainingTime); } else { resolve(result); } }); }); req.on('error', reject); }); } // 使用示例 queryDevice('D001') .then(result => console.log('设备数据:', result.data)) .catch(err => console.error('查询失败:', err)); ``` ### Python ```python import requests import time def query_device(device_number, retry_limit=3): """查询设备数据,自动处理限流重试""" url = 'http://localhost:8080/api/device/data' params = {'deviceNumber': device_number} retry_count = 0 while retry_count < retry_limit: try: response = requests.get(url, params=params) if response.status_code == 429: # 被限流,解析等待时间 message = response.json().get('message', '') import re match = re.search(r'(\d+)ms', message) wait_time = int(match.group(1)) / 1000 if match else 5 print(f'限流中,{wait_time}s 后重试...') time.sleep(wait_time) retry_count += 1 continue # 成功响应 data = response.json() return data.get('data', {}).get('data', {}) except Exception as e: print(f'请求失败: {e}') return None print('重试次数已用尽') return None # 使用示例 device_data = query_device('D001') if device_data: print('设备数据:', device_data) else: print('获取设备数据失败') ``` ### cURL ```bash # 基础请求 curl "http://localhost:8080/api/device/data?deviceNumber=D001" # 快速重复请求(会被限流) for i in {1..5}; do curl "http://localhost:8080/api/device/data?deviceNumber=D001" echo "请求 $i 完成" done # 查看限流状态 curl "http://localhost:8080/api/ratelimit/stats" # 清空限流记录 curl -X POST "http://localhost:8080/api/ratelimit/clear" ``` --- ## 生产环境建议 ### 1. 合理配置限流间隔 ```json { "rateLimit": { "interval": 10000 // 生产环境建议 10 秒 } } ``` ### 2. 监控限流情况 定期检查限流统计,了解请求模式: ```bash # 每 10 秒检查一次限流状态 while true; do curl "http://localhost:8080/api/ratelimit/stats" | jq . sleep 10 done ``` ### 3. 应用端重试策略 应用端应实现指数退避重试: ```javascript async function queryWithRetry(deviceNumber, maxRetries = 3) { for (let i = 0; i < maxRetries; i++) { try { const response = await fetch( `http://localhost:8080/api/device/data?deviceNumber=${deviceNumber}` ); if (response.status === 429) { const data = await response.json(); const message = data.message; const match = message.match(/(\d+)ms/); const waitTime = match ? parseInt(match[1]) : 5000; // 等待后重试 await new Promise(r => setTimeout(r, waitTime)); continue; } return await response.json(); } catch (err) { console.error(`重试 ${i + 1} 失败:`, err); } } throw new Error('所有重试都失败了'); } ``` ### 4. 日志监控 查看服务器日志中的限流信息: ```bash # 查看限流日志 tail -f logs/combined.log | grep "限流\|429" ``` --- ## 性能影响 ### 限流带来的影响 | 指标 | 值 | |------|-----| | 检查时间 | < 1ms | | 内存占用 | ~1KB/设备 | | CPU 开销 | 可忽略 | ### 可支持的规模 - **单个服务器** 可支持数千个不同设备的限流记录 - **QPS** 不受限流影响(限流只是简单的时间戳比较) --- ## 常见问题 ### Q1: 如何禁用限流? **A:** 在 `httpConfig.json` 中设置: ```json { "rateLimit": { "enabled": false } } ``` ### Q2: 限流是否对所有接口都适用? **A:** 不是。目前只对 `/api/device/data` 接口限流。其他接口(如 `/api/device/all`、`/api/device/list` 等)不受限流限制。 ### Q3: 我想为不同设备设置不同的限流时间? **A:** 当前版本不支持。建议在应用端实现设备级的限流逻辑。 ### Q4: 服务器重启后限流记录会丢失吗? **A:** 是的。限流记录存储在内存中,服务器重启后会清空。 ### Q5: 如何监控哪些设备经常被限流? **A:** 调用 `/api/ratelimit/stats` 接口查看,或查看程序日志中的⏱️记录。 ### Q6: 限流和 MQTT/阿里云 有关吗? **A:** 无关。限流只针对 HTTP API 接口,MQTT 和阿里云上传不受影响。 --- ## 故障排查 ### 问题: 所有请求都返回 429 **排查步骤:** 1. 检查 `httpConfig.json` 中的 `rateLimit.interval` 配置 2. 调用 `/api/ratelimit/clear` 清空限流记录 3. 检查系统时钟是否正确 ### 问题: 限流不生效 **排查步骤:** 1. 确认 `httpConfig.json` 中 `rateLimit.enabled` 为 `true` 2. 调用 `/api/ratelimit/stats` 确认限流已启用 3. 检查是否在 5 秒内进行了重复请求 ### 问题: 出现 500 错误 **排查步骤:** 1. 查看服务器日志 2. 检查是否有其他网络问题 3. 尝试清空限流记录后重试 --- **文档版本:** 1.0 **更新时间:** 2025年12月 **支持的 API 版本:** 1.0+