mm_os
Version:
MM_OS服务端架构,用于快速构建应用程序,支持网站建设、小程序后台、AI应用、物联网(IOT/AIOT)、游戏服务端等多种场景。
660 lines (569 loc) • 16.9 kB
JavaScript
const { Readable } = require('stream');
/**
* 自定义持久化类 - 为Aedes MQTT broker提供持久化存储支持
*/
class Persist {
/**
* 构造函数
* @param {object} config 配置参数
* @param {object} cache 缓存管理器
*/
constructor(config, cache) {
// 配置参数
this.config = {
// 缓存配置
cache: true,
// 缓存前缀
prefix: 'mqtt:',
// 缓存过期时间(毫秒)
ttl: 3600000,
...config
};
// 缓存管理器,提供set、get、del、keys功能
this._cache = cache;
if (!this._cache) {
throw new Error('缓存管理器不能为空');
}
// 验证缓存管理器是否实现了必要的方法
let req_methods = ['set', 'get', 'delete', 'keys'];
for (let method of req_methods) {
if (typeof this._cache[method] !== 'function') {
throw new Error(`缓存管理器必须实现${method}方法`);
}
}
}
}
/**
* 生成带前缀的缓存键
* @private
* @param {string} key - 原始键
* @returns {string} 带前缀的缓存键
*/
Persist.prototype._getCacheKey = function(key) {
return this.config.prefix + key;
};
/**
* 获取所有匹配模式的键
* @private
* @param {string} pattern - 键模式
* @returns {Array} 匹配的键数组
*/
Persist.prototype._getKeysByPattern = function(pattern) {
let prefix = this.config.prefix;
let regex = new RegExp('^' + prefix + pattern.replace(/\*/g, '.*') + '$');
return this._cache.keys().filter(key => regex.test(key)).map(key => key.slice(prefix.length));
};
// ------------------- 订阅管理 -------------------
/**
* 添加订阅
* @param {object} client - 客户端对象
* @param {object} sub - 订阅对象
* @param {Function} cb - 回调函数
*/
Persist.prototype.addSub = function(client, sub, cb) {
try {
if (!client || !client.clientId) {
throw new Error('无效的客户端对象');
}
if (!sub || !sub.topic) {
throw new Error('无效的订阅对象');
}
let key = this._getCacheKey(`subscription:${client.clientId}:${sub.topic}`);
this._cache.set(key, sub, this.config.ttl);
if (cb) cb(null);
} catch (error) {
if (cb) cb(error);
}
};
/**
* 删除订阅
* @param {object} client - 客户端对象
* @param {object} sub - 订阅对象
* @param {Function} cb - 回调函数
*/
Persist.prototype.removeSub = function(client, sub, cb) {
try {
if (!client || !client.clientId) {
throw new Error('无效的客户端对象');
}
if (!sub || !sub.topic) {
throw new Error('无效的订阅对象');
}
let key = this._getCacheKey(`subscription:${client.clientId}:${sub.topic}`);
this._cache.delete(key);
if (cb) cb(null);
} catch (error) {
if (cb) cb(error);
}
};
/**
* 删除客户端的所有订阅
* @param {object} client - 客户端对象
* @param {Function} cb - 回调函数
*/
Persist.prototype.removeSubs = function(client, cb) {
try {
if (!client || !client.clientId) {
throw new Error('无效的客户端对象');
}
let pat = `subscription:${client.clientId}:*`;
let keys = this._getKeysByPattern(pat);
// 同步执行所有删除操作
keys.forEach(key => {
this._cache.delete(this._getCacheKey(key));
});
if (cb) cb(null);
} catch (error) {
if (cb) cb(error);
}
};
/**
* 获取客户端的所有订阅
* @param {object} client - 客户端对象
* @param {Function} cb - 回调函数
*/
Persist.prototype.getSubs = function(client, cb) {
try {
if (!client || !client.clientId) {
throw new Error('无效的客户端对象');
}
let pattern = `subscription:${client.clientId}:*`;
let keys = this._getKeysByPattern(pattern);
// 同步执行所有获取操作
let subs = keys.map(key => {
return this._cache.get(this._getCacheKey(key));
}).filter(Boolean);
if (cb) cb(null, subs);
} catch (error) {
if (cb) cb(error);
}
};
/**
* 获取订阅了指定主题的所有客户端
* @param {string} topic - 主题
* @param {Function} cb - 回调函数
*/
Persist.prototype.subsByTopic = function(topic, cb) {
try {
if (!topic) {
throw new Error('主题不能为空');
}
// 简单实现,实际可能需要支持通配符匹配
let pat = `subscription:*:${topic}`;
let keys = this._getKeysByPattern(pat);
// 同步执行所有获取操作
let subs = keys.map(key => {
let parts = key.split(':');
if (parts.length >= 3) {
let cid = parts[1];
let sub = this._cache.get(this._getCacheKey(key));
if (sub) {
return {
client_id: cid,
sub: sub
};
}
}
return null;
}).filter(Boolean);
if (cb) cb(null, subs);
} catch (error) {
if (cb) cb(error);
}
};
// ------------------- 保留消息 -------------------
/**
* 存储保留消息
* @param {object} packet - MQTT消息包
* @param {Function} callback - 回调函数
*/
Persist.prototype.storeRetained = function(packet, callback) {
try {
if (!packet || !packet.topic) {
throw new Error('无效的消息包');
}
let key = this._getCacheKey(`retained:${packet.topic}`);
this._cache.set(key, packet, this.config.ttl);
if (callback) callback(null);
} catch (error) {
if (callback) callback(error);
}
};
/**
* 删除保留消息
* @param {string} topic - 主题
* @param {Function} callback - 回调函数
*/
Persist.prototype.cleanRetained = function(topic, callback) {
try {
if (!topic) {
throw new Error('主题不能为空');
}
let key = this._getCacheKey(`retained:${topic}`);
this._cache.delete(key);
if (callback) callback(null);
} catch (error) {
if (callback) callback(error);
}
};
/**
* 获取保留消息
* @param {string} topic - 主题
* @param {Function} cb - 回调函数
*/
Persist.prototype.getRetained = function(topic, cb) {
try {
if (!topic) {
throw new Error('主题不能为空');
}
let key = this._getCacheKey(`retained:${topic}`);
let retained = this._cache.get(key);
if (cb) cb(null, retained ? [retained] : []);
} catch (error) {
if (cb) cb(error);
}
};
// ------------------- 传输中消息 (QoS) -------------------
/**
* 存储传输中消息
* @param {object} client - 客户端对象
* @param {object} packet - MQTT消息包
* @param {Function} callback - 回调函数
*/
Persist.prototype.storePacket = function(client, packet, callback) {
try {
if (!client || !client.clientId) {
throw new Error('无效的客户端对象');
}
if (!packet || !packet.msgId) {
throw new Error('无效的消息包');
}
let key = this._getCacheKey(`packet:${client.clientId}:${packet.msgId}`);
this._cache.set(key, packet, this.config.ttl);
if (callback) callback(null);
} catch (error) {
if (callback) callback(error);
}
};
/**
* 删除传输中消息
* @param {object} client - 客户端对象
* @param {object} packet - MQTT消息包
* @param {Function} callback - 回调函数
*/
Persist.prototype.removePacket = function(client, packet, callback) {
try {
if (!client || !client.clientId) {
throw new Error('无效的客户端对象');
}
if (!packet || !packet.msgId) {
throw new Error('无效的消息包');
}
let key = this._getCacheKey(`packet:${client.clientId}:${packet.msgId}`);
this._cache.delete(key);
if (callback) callback(null);
} catch (error) {
if (callback) callback(error);
}
};
/**
* 获取传输中消息
* @param {object} client - 客户端对象
* @param {object} packet - MQTT消息包
* @param {Function} callback - 回调函数
*/
Persist.prototype.getPacket = function(client, packet, callback) {
try {
if (!client || !client.clientId) {
throw new Error('无效的客户端对象');
}
if (!packet || !packet.msgId) {
throw new Error('无效的消息包');
}
let key = this._getCacheKey(`packet:${client.clientId}:${packet.msgId}`);
if (callback) callback(null, this._cache.get(key));
} catch (error) {
if (callback) callback(error);
}
};
/**
* 获取客户端的所有传输中消息
* @param {object} client - 客户端对象
* @param {Function} callback - 回调函数
*/
Persist.prototype.getPackets = function(client, callback) {
try {
if (!client || !client.clientId) {
throw new Error('无效的客户端对象');
}
let pattern = `packet:${client.clientId}:*`;
let keys = this._getKeysByPattern(pattern);
// 同步执行所有获取操作
let packets = keys.map(key => {
let packet = this._cache.get(this._getCacheKey(key));
return packet;
}).filter(Boolean);
if (callback) callback(null, packets);
} catch (error) {
if (callback) callback(error);
}
};
/**
* 删除过期的传输中消息
* @param {object} packet - MQTT消息包
* @param {Function} callback - 回调函数
*/
Persist.prototype.delOutdatedPacket = function(packet, callback) {
try {
if (!packet || !packet.msgId) {
throw new Error('无效的消息包');
}
// 注意:这个方法可能需要客户端信息,这里简单处理
let pattern = `packet:*:${packet.msgId}`;
let keys = this._getKeysByPattern(pattern);
// 同步执行所有删除操作
keys.forEach(key => {
this._cache.delete(this._getCacheKey(key));
});
if (callback) callback(null);
} catch (error) {
if (callback) callback(error);
}
};
// ------------------- 遗嘱消息 -------------------
/**
* 存储遗嘱消息
* @param {object} client - 客户端对象
* @param {object} packet - MQTT消息包
* @param {Function} callback - 回调函数
*/
Persist.prototype.storeWill = function(client, packet, callback) {
try {
if (!client || !client.clientId) {
throw new Error('无效的客户端对象');
}
if (!packet) {
throw new Error('无效的消息包');
}
let key = this._getCacheKey(`will:${client.clientId}`);
this._cache.set(key, packet, this.config.ttl);
if (callback) callback(null);
} catch (error) {
if (callback) callback(error);
}
};
/**
* 删除遗嘱消息
* @param {object} client - 客户端对象
* @param {Function} callback - 回调函数
*/
Persist.prototype.removeWill = function(client, callback) {
try {
if (!client || !client.clientId) {
throw new Error('无效的客户端对象');
}
let key = this._getCacheKey(`will:${client.clientId}`);
this._cache.delete(key);
if (callback) callback(null);
} catch (error) {
if (callback) callback(error);
}
};
/**
* 获取遗嘱消息
* @param {object} client - 客户端对象
* @param {Function} callback - 回调函数
*/
Persist.prototype.getWill = function(client, callback) {
try {
if (!client || !client.clientId) {
throw new Error('无效的客户端对象');
}
let key = this._getCacheKey(`will:${client.clientId}`);
let will = this._cache.get(key);
if (callback) callback(null, will);
} catch (error) {
if (callback) callback(error);
}
};
// ------------------- 离线消息 -------------------
/**
* 存储离线消息
* @param {object} client - 客户端对象
* @param {object} packet - MQTT消息包
* @param {Function} callback - 回调函数
*/
Persist.prototype.storeOfflinePacket = function(client, packet, callback) {
try {
if (!client || !client.clientId) {
throw new Error('无效的客户端对象');
}
if (!packet || !packet.msgId) {
throw new Error('无效的消息包');
}
let key = this._getCacheKey(`offline:${client.clientId}:${packet.msgId}`);
this._cache.set(key, packet, this.config.ttl);
if (callback) callback(null);
} catch (error) {
if (callback) callback(error);
}
};
/**
* 获取客户端的所有离线消息
* @param {object} client - 客户端对象
* @param {Function} callback - 回调函数
*/
Persist.prototype.getOfflinePackets = function(client, callback) {
try {
if (!client || !client.clientId) {
throw new Error('无效的客户端对象');
}
let pattern = `offline:${client.clientId}:*`;
let keys = this._getKeysByPattern(pattern);
// 同步执行所有获取操作
let packets = keys.map(key => {
let packet = this._cache.get(this._getCacheKey(key));
return packet;
}).filter(Boolean);
if (callback) callback(null, packets);
} catch (error) {
if (callback) callback(error);
}
};
// ------------------- 流式恢复(重要!) -------------------
/**
* 处理保留消息
* @private
* @param {object} self 当前实例
* @param {object} stream 可读流
*/
Persist.prototype._procRetainMsgs = function(self, stream) {
let keys = self._getKeysByPattern('retained:*');
let msgs = keys.map(key => {
let packet = self._cache.get(self._getCacheKey(key));
if (packet) {
return {
type: 'retained',
topic: packet.topic,
payload: packet.payload,
qos: packet.qos,
retain: packet.retain
};
}
return null;
}).filter(Boolean);
msgs.forEach(msg => {
stream.push(msg);
});
};
/**
* 处理订阅信息
* @private
* @param {object} self 当前实例
* @param {object} stream 可读流
*/
Persist.prototype._procSubs = function(self, stream) {
let keys = self._getKeysByPattern('subscription:*:*');
let subs = keys.map(key => {
let parts = key.split(':');
if (parts.length >= 3) {
let cid = parts[1];
let sub = self._cache.get(self._getCacheKey(key));
if (sub) {
return {
type: 'subscription',
client_id: cid,
sub: sub
};
}
}
return null;
}).filter(Boolean);
subs.forEach(sub => {
stream.push(sub);
});
};
/**
* 处理遗嘱消息
* @private
* @param {object} self 当前实例
* @param {object} stream 可读流
*/
Persist.prototype._procWills = function(self, stream) {
let keys = self._getKeysByPattern('will:*');
let wills = keys.map(key => {
let parts = key.split(':');
if (parts.length >= 2) {
let cid = parts[1];
let will = self._cache.get(self._getCacheKey(key));
if (will) {
return {
type: 'will',
client_id: cid,
will: will
};
}
}
return null;
}).filter(Boolean);
wills.forEach(will => {
stream.push(will);
});
};
/**
* 处理传输中消息
* @private
* @param {object} self 当前实例
* @param {object} stream 可读流
*/
Persist.prototype._procInflightMsgs = function(self, stream) {
let keys = self._getKeysByPattern('packet:*:*');
let packets = keys.map(key => {
let parts = key.split(':');
if (parts.length >= 3) {
let cid = parts[1];
let packet = self._cache.get(self._getCacheKey(key));
if (packet) {
return {
type: 'inflight',
client_id: cid,
packet: packet
};
}
}
return null;
}).filter(Boolean);
packets.forEach(packet => {
stream.push(packet);
});
};
/**
* 创建可读流,用于恢复Aedes状态
* @returns {Readable} 可读流
*/
Persist.prototype.createStream = function() {
let self = this;
return new Readable({
obj_mode: true,
read() {
try {
// 处理保留消息
self._procRetainMsgs(self, this);
// 处理订阅信息
self._procSubs(self, this);
// 处理遗嘱消息
self._procWills(self, this);
// 处理传输中消息
self._procInflightMsgs(self, this);
// 结束流
this.push(null);
} catch (error) {
self._cache.log && self._cache.log('error', '创建恢复流时出错:', error);
this.push(null);
}
}
});
};
module.exports = {
Persist
};