UNPKG

occaecatidicta

Version:
243 lines (214 loc) 7.16 kB
import { getLogger, Logger } from 'omelox-logger'; let logger = getLogger('omelox-rpc', 'mqtt-acceptor'); import { EventEmitter } from 'events'; import { Tracer } from '../../util/tracer'; import * as utils from '../../util/utils'; let MqttCon: any = require('mqtt-connection'); import * as util from 'util'; import * as net from 'net'; import { Socket } from 'net'; import {AcceptorOpts, IAcceptor, AcceptorCallback} from '../acceptor'; import { MsgPkg } from '../dispatcher'; export interface AcceptorPkg { source: string; remote: string; id: string & number; seq: number; msg: MsgPkg; } let curId = 1; export class MQTTAcceptor extends EventEmitter implements IAcceptor { interval: number; // flush interval in ms bufferMsg: any; rpcLogger: any; rpcDebugLog: any; _interval: any; // interval object sockets: any; msgQueues: any; cb: AcceptorCallback; inited: boolean; server: net.Server; closed: boolean; constructor(opts: AcceptorOpts, cb: AcceptorCallback) { super(); this.interval = opts.interval; // flush interval in ms this.bufferMsg = opts.bufferMsg; this.rpcLogger = opts.rpcLogger; this.rpcDebugLog = opts.rpcDebugLog; this._interval = null; // interval object this.sockets = {}; this.msgQueues = {}; this.cb = cb; } listen(port: number|string) { // check status if (this.inited) { // this.cb(new Error('already inited.')); // return; throw new Error('already inited.'); } this.inited = true; let self = this; this.server = new net.Server(); this.server.listen(port); this.server.on('error', (err) => { logger.error('rpc server is error: %j', err.stack); self.emit('error', err, this); }); this.server.on('connection', function (stream) { stream.setNoDelay(true) let socket = MqttCon(stream); socket['id'] = curId++; socket.on('connect', function (pkg: any) { console.log('connected'); }); socket.on('publish', function (pkg: any) { pkg = pkg.payload.toString(); let isArray = false; try { pkg = JSON.parse(pkg); if (pkg instanceof Array) { self.processMsgs(socket, pkg); isArray = true; } else { self.processMsg(socket, pkg); } } catch (err) { if (!isArray) { self.doSend(socket, { id: pkg.id, resp: [self.cloneError(err)] }); } logger.error('process rpc message error %s', err.stack); } }); socket.on('pingreq', function () { socket.pingresp(); }); socket.on('error', function () { self.onSocketClose(socket); }); socket.on('close', function () { self.onSocketClose(socket); }); self.sockets[socket.id] = socket; socket.on('disconnect', function (reason: Error) { self.onSocketClose(socket); }); }); if (this.bufferMsg) { this._interval = setInterval(function () { self.flush(); }, this.interval); } } close() { if (this.closed) { return; } this.closed = true; if (this._interval) { clearInterval(this._interval); this._interval = null; } this.server.close(); this.emit('closed'); } onSocketClose(socket: {[key: string]: any}) { if (!socket['closed']) { let id = socket.id; socket['closed'] = true; delete this.sockets[id]; delete this.msgQueues[id]; } } cloneError(origin: {msg: string, stack: object}) { // copy the stack infos for Error instance json result is empty let res = { msg: origin.msg, stack: origin.stack }; return res; } processMsg(socket: object, pkg: AcceptorPkg) { let tracer: Tracer = null; if (this.rpcDebugLog) { tracer = new Tracer(this.rpcLogger, this.rpcDebugLog, pkg.remote, pkg.source, pkg.msg, pkg.id, pkg.seq); tracer.info('server', __filename, 'processMsg', 'mqtt-acceptor receive message and try to process message'); } this.cb(tracer, pkg.msg, (... args: any[]) => { let errorArg = args[0]; // first callback argument can be error object, the others are message if (errorArg && errorArg instanceof Error) { args[0] = this.cloneError(<any>errorArg); } let resp; if (tracer && tracer.isEnabled) { resp = { traceId: tracer.id, seqId: tracer.seq, source: tracer.source, id: pkg.id, resp: args }; } else { resp = { id: pkg.id, resp: args }; } if (this.bufferMsg) { this.enqueue(socket, resp); } else { this.doSend(socket, resp); } }); } processMsgs(socket: any, pkgs: Array<AcceptorPkg>) { for (let i = 0, l = pkgs.length; i < l; i++) { this.processMsg(socket, pkgs[i]); } } enqueue(socket: any, msg: {[key: string]: any}) { let id = socket.id; let queue = this.msgQueues[id]; if (!queue) { queue = this.msgQueues[id] = []; } queue.push(msg); } flush() { let sockets = this.sockets, queues = this.msgQueues, queue, socket; for (let socketId in queues) { socket = sockets[socketId]; if (!socket) { // clear pending messages if the socket not exist any more delete queues[socketId]; continue; } queue = queues[socketId]; if (!queue.length) { continue; } this.doSend(socket, queue); queues[socketId] = []; } } doSend(socket: any, msg: {[key: string]: any}) { socket.publish({ topic: 'rpc', payload: JSON.stringify(msg) }); } } /** * create acceptor * * @param opts init params * @param cb(tracer, msg, cb) callback function that would be invoked when new message arrives */ export function create(opts: AcceptorOpts, cb: (tracer: Tracer, msg?: any, cb?: Function) => void) { return new MQTTAcceptor(opts || <any>{}, cb); }