mydog
Version:
a framework of typescript game server
301 lines (272 loc) • 11.2 kB
text/typescript
/**
* After the non-master server is started, it connects to the master server, knows each other, and processes related logic
*/
import Application from "../application";
import { MonitorCli } from "./cliUtil";
import { TcpClient } from "./tcpClient";
import define = require("../util/define");
import { SocketProxy, monitor_get_new_server, monitor_remove_server, loggerLevel, monitor_reg_master, ServerInfo } from "../util/interfaceDefine";
import { encodeInnerData } from "./msgCoder";
import * as rpcClient from "./rpcClient";
import * as path from "path";
let meFilename = `[${path.basename(__filename, ".js")}.ts]`;
let serverIdsArr: string[] = [];
let hasStartAll = false;
export function start(_app: Application) {
new monitor_client_proxy(_app);
}
export class monitor_client_proxy {
private app: Application;
private socket: SocketProxy = null as any;
private monitorCli: MonitorCli;
private heartbeatTimer: NodeJS.Timeout = null as any;
private heartbeatTimeoutTimer: NodeJS.Timeout = null as any;
private removeDiffServers: { [id: string]: string } = {}; // After the monitor is reconnected, the server set to be compared and removed
private needDiff: boolean = false; // whether need to compare
private diffTimer: NodeJS.Timeout = null as any; // diff timeout
constructor(app: Application) {
this.app = app;
this.monitorCli = new MonitorCli(app);
this.doConnect(0);
let serversConfig = app.serversConfig;
for (let x in serversConfig) {
let arr = serversConfig[x];
for (let one of arr) {
serverIdsArr.push(one.id);
}
}
removeFromArr(serverIdsArr, app.serverId);
}
/**
* Connect master
*/
private doConnect(delay: number) {
let self = this;
setTimeout(function () {
let connectCb = function () {
self.app.logger(loggerLevel.debug, `${meFilename} connected to master success`);
// Register with the master
self.register();
// Heartbeat package
self.heartbeat();;
};
self.app.logger(loggerLevel.debug, `${meFilename} try to connect to master now`);
self.socket = new TcpClient(self.app.masterConfig.port, self.app.masterConfig.host, define.some_config.SocketBufferMaxLen, false, connectCb);
self.socket.on("data", self.onData.bind(self));
self.socket.on("close", self.onClose.bind(self));
}, delay);
}
/**
* register
*/
private register() {
let tokenConfig = this.app.someconfig.recognizeToken || {};
let serverToken = tokenConfig.serverToken || define.some_config.Server_Token;
let loginInfo: monitor_reg_master = {
T: define.Monitor_To_Master.register,
serverInfo: this.app.serverInfo,
serverToken: serverToken
};
this.send(loginInfo);
}
/**
* Received the msg
*/
private onData(_data: Buffer) {
try {
let data: any = JSON.parse(_data.toString());
if (data.T === define.Master_To_Monitor.addServer) {
this.addServer((data as monitor_get_new_server).servers);
} else if (data.T === define.Master_To_Monitor.removeServer) {
this.removeServer(data as monitor_remove_server);
} else if (data.T === define.Master_To_Monitor.cliMsg) {
this.monitorCli.deal_master_msg(this, data);
} else if (data.T === define.Master_To_Monitor.heartbeatResponse) {
clearTimeout(this.heartbeatTimeoutTimer);
this.heartbeatTimeoutTimer = null as any;
}
}
catch (e: any) {
this.app.logger(loggerLevel.error, e);
}
}
/**
* closed
*/
private onClose() {
this.app.logger(loggerLevel.error, `${meFilename} socket closed, try to reconnect master later`);
this.needDiff = true;
this.removeDiffServers = {};
clearTimeout(this.diffTimer);
clearTimeout(this.heartbeatTimer);
clearTimeout(this.heartbeatTimeoutTimer);
this.heartbeatTimeoutTimer = null as any;
this.doConnect(define.some_config.Time.Monitor_Reconnect_Time * 1000);
}
/**
* Send heartbeat
*/
private heartbeat() {
let timeDelay = define.some_config.Time.Monitor_Heart_Beat_Time * 1000 - 5000 + Math.floor(5000 * Math.random());
this.heartbeatTimer = setTimeout(() => {
let heartbeatMsg = { "T": define.Monitor_To_Master.heartbeat };
this.send(heartbeatMsg);
this.heartbeatTimeout();
this.heartbeatTimer.refresh();
}, timeDelay)
}
/**
* Heartbeat timeout
*/
private heartbeatTimeout() {
if (this.heartbeatTimeoutTimer !== null) {
return;
}
let self = this;
this.heartbeatTimeoutTimer = setTimeout(function () {
self.app.logger(loggerLevel.error, `${meFilename} heartbeat timeout, close the socket`);
self.socket.close();
}, define.some_config.Time.Monitor_Heart_Beat_Timeout_Time * 1000)
}
/**
* Send message (not buffer)
*/
send(msg: any) {
this.socket.send(encodeInnerData(msg));
}
/**
* Add server
*/
private addServer(servers: { [id: string]: ServerInfo }) {
if (this.needDiff) {
this.diffTimerStart();
}
let serversApp = this.app.servers;
let serversIdMap = this.app.serversIdMap;
let serverInfo: ServerInfo;
for (let sid in servers) {
serverInfo = servers[sid];
if (this.needDiff) {
this.addOrRemoveDiffServer(serverInfo.id, true, serverInfo.serverType);
}
let tmpServer: ServerInfo = serversIdMap[serverInfo.id];
if (tmpServer && tmpServer.host === serverInfo.host && tmpServer.port === serverInfo.port) { // If it already exists and the ip configuration is the same, ignore it (other configurations are not considered, please guarantee by the developer)
continue;
}
if (!serversApp[serverInfo.serverType]) {
serversApp[serverInfo.serverType] = [];
}
if (!!tmpServer) {
for (let i = serversApp[serverInfo.serverType].length - 1; i >= 0; i--) {
if (serversApp[serverInfo.serverType][i].id === tmpServer.id) {
serversApp[serverInfo.serverType].splice(i, 1);
rpcClient.removeSocket(tmpServer.id);
this.emitRemoveServer(tmpServer);
break;
}
}
}
serversApp[serverInfo.serverType].push(serverInfo);
serversIdMap[serverInfo.id] = serverInfo;
this.emitAddServer(serverInfo);
rpcClient.ifCreateRpcClient(this.app, serverInfo)
}
}
/**
* Remove server
*/
private removeServer(msg: monitor_remove_server) {
if (this.needDiff) {
this.diffTimerStart();
this.addOrRemoveDiffServer(msg.id, false);
}
delete this.app.serversIdMap[msg.id];
let serversApp = this.app.servers;
if (serversApp[msg.serverType]) {
for (let i = 0; i < serversApp[msg.serverType].length; i++) {
if (serversApp[msg.serverType][i].id === msg.id) {
let tmpInfo = serversApp[msg.serverType][i];
serversApp[msg.serverType].splice(i, 1);
rpcClient.removeSocket(msg.id)
this.emitRemoveServer(tmpInfo);
break;
}
}
}
}
private addOrRemoveDiffServer(sid: string, add: boolean, serverType?: string) {
if (add) {
this.removeDiffServers[sid] = serverType as string;
} else {
delete this.removeDiffServers[sid];
}
}
private diffTimerStart() {
clearTimeout(this.diffTimer);
let self = this;
this.diffTimer = setTimeout(function () {
self.diffFunc();
}, 5000); // Compare after 5 seconds
}
/**
* 比对原因:与master断开连接期间,如果另一台逻辑服挂了,本服不能断定该服是否移除,
* 因为添加和删除统一由master通知,所以与master断开期间,不可更改与其他服的关系,
* 待本服重新连接上master后,通过比对,移除无效服务器
*
* (Reason for comparison: During the disconnection from the master, if another logical server hangs up,
* the server cannot determine whether the server will be removed, because the addition and deletion are uniformly notified by the master,
* so during the disconnection from the master, it cannot be changed. Server relationship,
* after the server reconnects to the master, through the comparison, remove the invalid server)
*/
private diffFunc() {
this.needDiff = false;
let servers = this.app.servers;
for (let serverType in servers) {
for (let i = servers[serverType].length - 1; i >= 0; i--) {
let id = servers[serverType][i].id;
if (id === this.app.serverId) {
continue;
}
if (!this.removeDiffServers[id]) {
let tmpInfo = this.app.serversIdMap[id];
delete this.app.serversIdMap[id];
servers[serverType].splice(i, 1);
rpcClient.removeSocket(id);
this.emitRemoveServer(tmpInfo);
}
}
}
this.removeDiffServers = {};
}
/**
* Launch add server event
*/
private emitAddServer(serverInfo: ServerInfo) {
process.nextTick(() => {
this.app.emit("onAddServer", serverInfo);
});
if (!hasStartAll) {
removeFromArr(serverIdsArr, serverInfo.id);
if (serverIdsArr.length === 0) {
hasStartAll = true;
process.nextTick(() => {
this.app.emit("onStartAll");
});
}
}
}
/**
* Launch remove server event
*/
private emitRemoveServer(serverInfo: ServerInfo) {
process.nextTick(() => {
this.app.emit("onRemoveServer", serverInfo);
});
}
}
function removeFromArr<T = any>(arr: T[], one: T) {
let index = arr.indexOf(one);
if (index !== -1) {
arr.splice(index, 1);
}
}