UNPKG

mm_os

Version:

MM_OS服务端架构,用于快速构建应用程序,支持网站建设、小程序后台、AI应用、物联网(IOT/AIOT)、游戏服务端等多种场景。

660 lines (569 loc) 16.9 kB
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 };