import { Pool, PoolOptions } from './Pool'; import errorCodes from '../error'; import createDebuggerLogger from '../debugLogger'; import { Callback } from '../../constants'; const debuggerLogger = createDebuggerLogger('iot-logger-Writeable'); const { ERR_INVALID_OPT_VALUE, ERR_METHOD_NOT_IMPLEMENTED, } = errorCodes; const DEFAULT_BATCH_CONSUME_NUM = 10; const DEFAULT_CONSUME_CHECK_INTERVAL = 2000; const DEFAULT_MAX_CONSUME_IDLE_TIME = 3000; export interface WriteableOptions extends PoolOptions { batchConsume?: boolean; consumeNum?: number; consumeImme?: boolean; consumeCheckInterval?: number; maxConsumeIdleTime?: number; } export class Writeable extends Pool { protected readonly batchConsume: boolean; protected readonly consumeNum: number; protected readonly consumeCheckInterval?: number; protected readonly maxConsumeIdleTime?: number; private consumeImme?: boolean; private lastConsumeTime = 0; private checkTid; private isWriting = false; private isClosing = false; private isStopping = false; private writeLen = 0; // 类内部每次消费完触发的回调,即传入到_write的cb private readonly onAfterWrite: Callback; // 缓存当前处理的chunk完成时触发的cb(即外部调用write时传入的cb) private writeCb: Callback; // close函数被触发并且当前所有chunk已消费后触发的回调 private closeCb: Callback; // flush相关逻辑 private flushPromise: Promise; private emptyCb: Callback; constructor({ batchConsume, consumeNum, consumeImme = true, consumeCheckInterval, maxConsumeIdleTime, ...opts }: WriteableOptions = {}) { super(opts); this.batchConsume = !!batchConsume; if (consumeNum) { if ((Math.floor(consumeNum) !== consumeNum) || consumeNum < 0) { throw new ERR_INVALID_OPT_VALUE('consumeNum', consumeNum); } } if (!this.batchConsume) { this.consumeNum = 1; } else { this.consumeNum = consumeNum || DEFAULT_BATCH_CONSUME_NUM; } this.consumeImme = !!consumeImme; this.consumeCheckInterval = consumeCheckInterval || DEFAULT_CONSUME_CHECK_INTERVAL; this.maxConsumeIdleTime = Math.max(maxConsumeIdleTime || DEFAULT_MAX_CONSUME_IDLE_TIME, this.consumeCheckInterval); this.onAfterWrite = (err) => { this.delChunks(this.writeLen); this.writeLen = 0; this.isWriting = false; if (this.consumeImme) { Promise.resolve().then(() => this.doWrite()); } if (typeof this.writeCb === 'function') { this.writeCb(err); } this.writeCb = null; if (this.chunkLinkListLen <= 0) { typeof this.emptyCb === 'function' && this.emptyCb(); if (this.isClosing && typeof this.closeCb === 'function') { this.closeCb(); } } } this.closeCb = (_err) => { try { this.stopPollingCheck(); this._destroy(); } catch (err) { debuggerLogger.error('exec _destroy err', err); } finally { // 最多仅能触发一次 this.closeCb = null; } } if (!this.consumeImme) { this.startPollingCheck(); } } start() { debuggerLogger.info('start manually'); this.isStopping = false; this.doWrite(); } stop() { debuggerLogger.info('stop manually'); this.isStopping = true; } write(chunk, cb?) { if (this.isClosing) { debuggerLogger.warn('can\'t write after closed'); return false; } const ret = this.addChunk(chunk, cb); if (this.consumeImme) { this.doWrite(); } return ret; } close() { this.isClosing = true; } flush(): Promise { if (this.chunkLinkListLen === 0) { return; } if (!this.flushPromise) { this.flushPromise = new Promise((resolve) => { let consumeImmeCache = this.consumeImme; this.emptyCb = () => { resolve(); this.emptyCb = null; this.flushPromise = null; // 清空后,恢复之前设置的值 this.consumeImme = consumeImmeCache; consumeImmeCache = null; }; // 用户调用flush,表明想尽快清空pool,因此将标志位consumeImme强制设置为true this.consumeImme = true; this.doWrite(); }); } return this.flushPromise; } startPollingCheck() { if (this.consumeImme || this.isClosing) { return; } this.stopPollingCheck(); this.checkTid = setInterval(() => { if (this.chunkLinkListLen >= this.consumeNum || +new Date() - this.lastConsumeTime >= this.maxConsumeIdleTime) { this.lastConsumeTime = +new Date(); this.doWrite(); } }, this.consumeCheckInterval); } stopPollingCheck() { clearInterval(this.checkTid); } private doWrite() { if (this.isWriting || this.isStopping) { return; } let chunks = this.getChunks(this.consumeNum); if (chunks.length <= 0) { return; } this.isWriting = true; this.writeLen = chunks.length; const chunkDataList = chunks.map(item => item.chunk); try { // 批量消费 if (this.batchConsume) { this.writeCb = (err) => { chunks.forEach(item => { try { typeof item.callback === 'function' && item.callback(err); } catch (err) { debuggerLogger.error('exec write cb err', err); } }); chunks = null; } this._writev(chunkDataList, this.onAfterWrite); return; } // 单个消费 if (typeof chunks[0].callback === 'function') { let cb = chunks[0].callback; this.writeCb = (err) => { try { cb(err); } catch (err) { debuggerLogger.error('exec write cb err', err); } cb = null; } } this._write(chunkDataList[0], this.onAfterWrite); } catch (err) { debuggerLogger.error(`exec ${this.batchConsume ? '_writev' : '_write'} err`, err); this.onAfterWrite(err); } } // 单个处理,由外部实现具体逻辑 protected _write(_chunk: unknown, _cb: Callback) { throw new ERR_METHOD_NOT_IMPLEMENTED('_write()'); } // 批量处理,由外部实现具体逻辑 protected _writev(_chunks: Array, _cb: Callback) { throw new ERR_METHOD_NOT_IMPLEMENTED('_writev()'); } // 销毁,由外部实现具体逻辑 protected _destroy() { // 不要抛错(非关键路径) debuggerLogger.warn(new ERR_METHOD_NOT_IMPLEMENTED('_destroy()')); } }