import { singleton } from '../decorators/singleton.decorator' import { Action, getNodeList, listen } from './discovery' import { error, sysLog } from '../common/Debug' import { Config } from '../cfg/Config' import ip from 'internal-ip' import { RedisClient } from '../redis/RedisClient' const config: Config = require('../../config/config.json') const isProd = process.env.NODE_ENV === 'production' const NODES_SET = 'colyseus:nodes' const ROOM_COUNT_KEY = 'roomcount' const DISCOVERY_CHANNEL = 'colyseus:nodes:discovery'; interface Node { processId: string; address?: string } @singleton export class Service { /** * info server的key名 * @type {string} */ public static readonly INFO_NODE = 'poker:infosvr' /** * info server发布的channel * @type {string} */ public static readonly INFO_CHANNEL = 'poker:infosvr:discovery' /** * robot server的key名 * @type {string} */ public static readonly ROBOT_NODE = 'poker:robot' /** * robot server发布的channel * @type {string} */ public static readonly ROBOT_CHANNEL = 'poker:robot:discovery' public selfHost: string public serviceMap: Map = new Map() constructor() { this.serviceMap.set(Service.INFO_NODE, []) this.serviceMap.set(Service.ROBOT_NODE, []) if (isProd) { this.discoveryServices() } } public discoveryServices() { listen(Service.INFO_CHANNEL, (action: Action, address: string) => { sysLog('LISTEN: info channel ', action, address) if (action === 'add') { this.register(Service.INFO_NODE, address) } else if (action == 'remove') { this.unregister(Service.INFO_NODE, address) } }).then(() => { sysLog('subscribe to info channel success') }).catch(err => { sysLog('subscribe to info channel error', err) }) listen(Service.ROBOT_CHANNEL, (action: Action, address: string) => { sysLog('LISTEN: robot channel', action, address) if (action === 'add') { this.register(Service.ROBOT_NODE, address) } else if (action == 'remove') { this.unregister(Service.ROBOT_NODE, address) } }).then(() => { sysLog('subscribe to robot channel success') }).catch(err => { sysLog('subscribe to robot channel error', err) }) listen(DISCOVERY_CHANNEL, async (action: Action, message: string) => { await this.cleanRedisData() }).catch(err => {}) } public async getInfoSvr() { let key = Service.INFO_NODE if (!this.serviceMap.has(key) || this.serviceMap.get(key).length == 0) { let svrList = await getNodeList(key) this.serviceMap.set(key, svrList) } let svrList = this.serviceMap.get(key) if (svrList.length == 0 || !isProd) { error('no info service found') return config.info_svr } return 'http://' + svrList.randomOne() + '/svr' } public async getRobotSvr() { let key = Service.ROBOT_NODE if (!this.serviceMap.has(key) || this.serviceMap.get(key).length == 0) { let svrList = await getNodeList(key) this.serviceMap.set(key, svrList) } let svrList = this.serviceMap.get(key) if (svrList.length == 0 || !isProd) { error('no robot service found') return 'http://127.0.0.1:2500' } return 'http://' + svrList.randomOne() } public async registSelf(port: number) { const host = process.env.SELF_HOSTNAME || await ip.v4() this.selfHost = `ws://${ host }:${ port }` } private register(type: string, address: string) { let svrList = this.serviceMap.get(type) svrList.pushOnce(address) } private unregister(type: string, address: string) { let svrList = this.serviceMap.get(type) svrList.removeEx(address) } /** * 清理redis中roomcount的值 * @return {Promise} */ public async cleanRedisData() { const client = new RedisClient() const parseNode = function (data: string): Node { const [processId, address] = data.split("/"); return { processId, address }; } const nodeStrs: string[] = await client.smembers(NODES_SET) const nodes = nodeStrs.map(data => parseNode(data)) const nodeMap = nodes.toMap('processId') const countObjs = await client.hgetall(ROOM_COUNT_KEY) for (let key of Object.keys(countObjs)) { if (!nodeMap.has(key)) { await client.hdel(ROOM_COUNT_KEY, key) } } } }