东丽网口版透析机 socket- server 通讯
编辑 | blame | 历史 | 原始文档

HTTP 限流功能指南

概述

为了保护服务器性能和防止恶意请求,系统实现了基于设备号的请求限流功能。相同的设备号在指定时间间隔内只能发起一次请求。


配置

httpConfig.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秒)

常见配置

无限流限制

{
    "rateLimit": {
        "enabled": false
    }
}

严格限流(10秒)

{
    "rateLimit": {
        "enabled": true,
        "interval": 10000
    }
}

宽松限流(2秒)

{
    "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" }


测试

运行基础限流测试

node rateLimitTest.js

这会执行 9 个测试用例,验证:
1. ✅ 获取限流配置
2. ✅ 清空限流记录
3. ✅ 首次请求成功
4. ✅ 立即重复请求被限流
5. ✅ 部分间隔后仍被限流
6. ✅ 完整间隔后请求成功
7. ✅ 不同设备限流独立
8. ✅ 获取限流统计
9. ✅ 清空后可立即请求

运行压力测试

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

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

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

# 基础请求
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. 合理配置限流间隔

{
    "rateLimit": {
        "interval": 10000  // 生产环境建议 10 秒
    }
}

2. 监控限流情况

定期检查限流统计,了解请求模式:

# 每 10 秒检查一次限流状态
while true; do
  curl "http://localhost:8080/api/ratelimit/stats" | jq .
  sleep 10
done

3. 应用端重试策略

应用端应实现指数退避重试:

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. 日志监控

查看服务器日志中的限流信息:

# 查看限流日志
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.jsonrateLimit.enabledtrue
2. 调用 /api/ratelimit/stats 确认限流已启用
3. 检查是否在 5 秒内进行了重复请求

问题: 出现 500 错误

排查步骤:
1. 查看服务器日志
2. 检查是否有其他网络问题
3. 尝试清空限流记录后重试


文档版本: 1.0
更新时间: 2025年12月
支持的 API 版本: 1.0+