Press n or j to go to the next uncovered block, b, p or k for the previous block.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 | 57x 57x 57x 57x 57x 57x 57x 57x 57x 57x 57x 57x 57x 57x 57x 57x 57x 57x 57x 57x 57x 57x 57x 57x 57x 57x 57x 57x 33x 33x 33x 33x 33x 33x 33x 33x 33x 33x 33x 33x 33x 33x 99x 99x 33x 33x 33x 33x 33x 33x 33x 33x 33x 33x 33x 33x 33x 33x 33x 33x 33x 33x 33x 33x 75x 33x 33x 33x 33x 33x 33x 33x 33x 33x 33x 33x 33x 33x 33x 33x 33x 33x 33x 33x 33x 33x 33x 33x 33x 33x 33x 33x 33x 33x 33x 33x 33x 33x 33x 33x 33x 33x 33x 33x 33x 33x 33x 33x 33x 33x 33x 33x | import 'reflect-metadata'; import { createServer } from 'http'; import * as Koa from 'koa'; import { Server as SocketIO } from 'socket.io'; import * as KoaBodyParser from 'koa-bodyparser'; import { Middleware } from 'redis-smq-monitor-client'; import { v4 as uuid } from 'uuid'; import * as cors from '@koa/cors'; import * as stoppable from 'stoppable'; import { promisifyAll } from 'bluebird'; import { errorHandler } from './middlewares/error-handler'; import { getApplicationRouter } from './common/routing'; import { apiController } from './controllers/api/api-controller'; import { redisKeys } from './common/redis-keys/redis-keys'; import { events } from 'redis-smq'; import { TConfig } from '../types'; import { errors, PowerManager, RedisClient, WorkerPool, WorkerRunner, } from 'redis-smq-common'; import { ICompatibleLogger } from 'redis-smq-common/dist/types'; import { createRedisInstance } from './lib/redis'; import { getNamespacedLoggerInstance } from './lib/logger'; import { registry } from './lib/registry'; import { destroyQueueManager, getQueueManagerInstance, } from './lib/queue-manager'; import { destroyMessageManager, getMessageManagerInstance, } from './lib/message-manager'; import TimeSeriesCleanUpWorker from './workers/time-series.clean-up.worker'; import WebsocketHeartbeatStreamWorker from './workers/websocket-heartbeat-stream.worker'; import WebsocketMainStreamWorker from './workers/websocket-main-stream.worker'; import WebsocketOnlineStreamWorker from './workers/websocket-online-stream.worker'; import WebsocketRateStreamWorker from './workers/websocket-rate-stream.worker'; export class MonitorServer { protected static instance: MonitorServer | null = null; protected config; protected powerManager; protected logger; protected instanceId: string; protected workerRunner: WorkerRunner | null = null; protected application: { app: Koa; socketIO: SocketIO; httpServer: ReturnType<typeof stoppable>; } | null = null; protected redisClient: RedisClient | null = null; protected subscribeClient: RedisClient | null = null; private constructor(config: TConfig, logger: ICompatibleLogger) { this.instanceId = uuid(); this.config = config; this.logger = logger; this.powerManager = new PowerManager(false); registry.register('config', this.config); registry.register('logger', this.logger); } protected getRequiredProperty<K extends keyof MonitorServer>( key: K, ): MonitorServer[K] { Iif (!this[key]) { throw new errors.PanicError(`Expected a non null value of key [${key}]`); } return this[key]; } protected getApplication() { Iif (!this.application) { throw new errors.PanicError(`Expected a non null value.`); } return this.application; } protected getSubscribeClient(): RedisClient { Iif (!this.subscribeClient) { throw new errors.PanicError(`Expected a non null value.`); } return this.subscribeClient; } protected getRedisClient(): RedisClient { Iif (!this.redisClient) { throw new errors.PanicError(`Expected a non null value.`); } return this.redisClient; } protected getWorkerRunner(): WorkerRunner { Iif (!this.workerRunner) { throw new errors.PanicError(`Expected a non null value.`); } return this.workerRunner; } protected async bootstrap() { this.redisClient = await createRedisInstance(this.config); registry.register('redis', this.redisClient); const { socketOpts = {}, basePath } = this.config.server ?? {}; const app = new Koa(); app.use(errorHandler); app.use(KoaBodyParser()); app.use(Middleware(['/api/', '/socket.io/'], basePath)); app.use( cors({ origin: '*', }), ); const router = getApplicationRouter(app, [apiController]); app.use(router.routes()); app.use(router.allowedMethods()); const httpServer = stoppable(createServer(app.callback())); const socketIO = new SocketIO(httpServer, { ...socketOpts, cors: { origin: '*', }, }); this.application = { httpServer, socketIO, app, }; return this.application; } protected async subscribe(socketIO: SocketIO): Promise<void> { this.subscribeClient = await createRedisInstance(this.config); this.subscribeClient.psubscribe('stream*'); this.subscribeClient.on('pmessage', (pattern, channel, message) => { socketIO.emit(channel, JSON.parse(message)); }); } protected async runWorkers(): Promise<void> { const { keyLockMonitorServerWorkers } = redisKeys.getMainKeys(); this.workerRunner = new WorkerRunner( this.getRedisClient(), keyLockMonitorServerWorkers, new WorkerPool(), this.logger, ); this.workerRunner.on(events.ERROR, (err: Error) => { throw err; }); const redisClient = this.getRedisClient(); const queueManager = await getQueueManagerInstance(this.config); const messageManager = await getMessageManagerInstance(this.config); this.workerRunner.addWorker( new TimeSeriesCleanUpWorker(redisClient, queueManager, true), ); this.workerRunner.addWorker( new WebsocketHeartbeatStreamWorker(redisClient, true), ); this.workerRunner.addWorker( new WebsocketMainStreamWorker( redisClient, messageManager, queueManager, true, ), ); this.workerRunner.addWorker( new WebsocketOnlineStreamWorker(redisClient, queueManager, true), ); this.workerRunner.addWorker( new WebsocketRateStreamWorker(redisClient, queueManager, true), ); await new Promise((resolve) => { this.workerRunner?.once(events.UP, resolve); this.workerRunner?.run(); }); } async listen(): Promise<boolean> { const { host = '0.0.0.0', port = 7210 } = this.config.server ?? {}; const r = this.powerManager.goingUp(); if (r) { this.logger.info('Going up...'); const { socketIO, httpServer } = await this.bootstrap(); await this.subscribe(socketIO); await this.runWorkers(); await new Promise<void>((resolve) => { httpServer.listen(port, host, resolve); }); this.powerManager.commit(); this.logger.info(`Instance ID is ${this.instanceId}.`); this.logger.info(`Up and running on ${host}:${port}...`); return true; } return false; } async quit(): Promise<boolean> { const r = this.powerManager.goingDown(); if (r) { this.logger.info('Going down...'); const { httpServer } = this.getApplication(); await new Promise((resolve) => httpServer.stop(resolve)); await promisifyAll(this.getWorkerRunner()).quitAsync(); await promisifyAll(this.getSubscribeClient()).haltAsync(); await promisifyAll(this.getRedisClient()).haltAsync(); await destroyMessageManager(); await destroyQueueManager(); registry.reset(); this.workerRunner = null; this.application = null; this.powerManager.commit(); this.logger.info('Down.'); return true; } return false; } static createInstance(config: TConfig = {}): MonitorServer { if (!MonitorServer.instance) { const logger = getNamespacedLoggerInstance(config, 'monitor'); MonitorServer.instance = new MonitorServer(config, logger); } return MonitorServer.instance; } } |