// rateLimiter.js - 请求频率限制管理
|
const logger = require('./logger');
|
|
class RateLimiter {
|
constructor() {
|
// 存储请求记录
|
// key: identifier (设备号或IP), value: { lastRequestTime, count }
|
this.requestMap = new Map();
|
}
|
|
/**
|
* 检查是否允许请求
|
* @param {string} identifier - 标识符(设备号、IP 等)
|
* @param {number} intervalMs - 时间间隔(毫秒)
|
* @returns {object} { allowed: boolean, remainingTime: number }
|
*/
|
checkLimit(identifier, intervalMs = 5000) {
|
if (!identifier) {
|
logger.warn('限流检查: 标识符为空');
|
return { allowed: true, remainingTime: 0 };
|
}
|
|
const now = Date.now();
|
const record = this.requestMap.get(identifier);
|
|
if (!record) {
|
// 第一次请求,允许
|
this.requestMap.set(identifier, {
|
lastRequestTime: now,
|
count: 1
|
});
|
logger.info(`✅ 限流: 首次请求 ${identifier}`);
|
return { allowed: true, remainingTime: 0 };
|
}
|
|
const timeSinceLastRequest = now - record.lastRequestTime;
|
|
if (timeSinceLastRequest < intervalMs) {
|
// 请求过于频繁
|
const remainingTime = intervalMs - timeSinceLastRequest;
|
logger.warn(`⏱️ 限流: ${identifier} 请求过于频繁,需等待 ${remainingTime}ms`);
|
|
return {
|
allowed: false,
|
remainingTime: Math.ceil(remainingTime)
|
};
|
}
|
|
// 允许请求,更新记录
|
this.requestMap.set(identifier, {
|
lastRequestTime: now,
|
count: record.count + 1
|
});
|
logger.info(`✅ 限流: ${identifier} 请求已允许 (第 ${record.count + 1} 次)`);
|
|
return { allowed: true, remainingTime: 0 };
|
}
|
|
/**
|
* 获取限流状态
|
*/
|
getStatus(identifier) {
|
return this.requestMap.get(identifier) || null;
|
}
|
|
/**
|
* 获取所有限流记录
|
*/
|
getAllStatus() {
|
const result = {};
|
for (const [key, value] of this.requestMap.entries()) {
|
result[key] = value;
|
}
|
return result;
|
}
|
|
/**
|
* 清空特定标识符的限流记录
|
*/
|
clearIdentifier(identifier) {
|
if (this.requestMap.has(identifier)) {
|
this.requestMap.delete(identifier);
|
logger.info(`🗑️ 已清空限流记录: ${identifier}`);
|
return true;
|
}
|
return false;
|
}
|
|
/**
|
* 清空所有限流记录
|
*/
|
clearAll() {
|
const count = this.requestMap.size;
|
this.requestMap.clear();
|
logger.info(`🗑️ 已清空所有限流记录 (共 ${count} 条)`);
|
}
|
|
/**
|
* 获取限流统计信息
|
*/
|
getStats() {
|
return {
|
totalIdentifiers: this.requestMap.size,
|
records: this.getAllStatus()
|
};
|
}
|
|
/**
|
* 清理过期的限流记录(可选)
|
* @param {number} maxAgeMs - 最大保留时间(毫秒)
|
*/
|
cleanupExpired(maxAgeMs = 3600000) { // 默认 1 小时
|
const now = Date.now();
|
let cleanedCount = 0;
|
|
for (const [identifier, record] of this.requestMap.entries()) {
|
if (now - record.lastRequestTime > maxAgeMs) {
|
this.requestMap.delete(identifier);
|
cleanedCount++;
|
}
|
}
|
|
if (cleanedCount > 0) {
|
logger.info(`🧹 已清理过期限流记录: ${cleanedCount} 条`);
|
}
|
|
return cleanedCount;
|
}
|
}
|
|
// 导出单例
|
module.exports = new RateLimiter();
|