From b944d26044a06ac7154b6cccd9e155ae07f9d601 Mon Sep 17 00:00:00 2001 From: zhl Date: Sat, 30 Jan 2021 12:13:42 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=9C=8D=E5=8A=A1=E5=8F=91?= =?UTF-8?q?=E7=8E=B0=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 161 +++++++++++++++++++++++++++++++++++++++ package.json | 1 + src/api.server.ts | 109 +++++++++++++++----------- src/utils/system.util.ts | 40 ++++++++++ 4 files changed, 265 insertions(+), 46 deletions(-) create mode 100644 src/utils/system.util.ts diff --git a/package-lock.json b/package-lock.json index 4cfccc6..f16f896 100644 --- a/package-lock.json +++ b/package-lock.json @@ -272,6 +272,16 @@ "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", "dev": true }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, "dateformat": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz", @@ -290,6 +300,14 @@ "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==" }, + "default-gateway": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz", + "integrity": "sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==", + "requires": { + "execa": "^5.0.0" + } + }, "denque": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/denque/-/denque-1.5.0.tgz", @@ -327,6 +345,22 @@ "safe-buffer": "^5.0.1" } }, + "execa": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.0.0.tgz", + "integrity": "sha512-ov6w/2LCiuyO4RLYGdpFGjkcs0wMTgGE8PrkTHikeUy5iJekXyPIKUjifk5CsE0pt7sMCrMZ3YNqoCj6idQOnQ==", + "requires": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + } + }, "express-fileupload": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/express-fileupload/-/express-fileupload-1.2.0.tgz", @@ -564,6 +598,11 @@ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, + "get-stream": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.0.tgz", + "integrity": "sha512-A1B3Bh1UmL0bidM/YX2NsCOTnGJePL9rO/M+Mw3m9f2gUpfokS0hi5Eah0WSUEWZdZhIZtMjkIYS7mDfOqNHbg==" + }, "glob": { "version": "7.1.6", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", @@ -594,6 +633,11 @@ "toidentifier": "1.0.0" } }, + "human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==" + }, "iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -621,16 +665,50 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, + "internal-ip": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-6.2.0.tgz", + "integrity": "sha512-D8WGsR6yDt8uq7vDMu7mjcR+yRMm3dW8yufyChmszWRjcSHuxLBkR3GdS2HZAjodsaGuCvXeEJpueisXJULghg==", + "requires": { + "default-gateway": "^6.0.0", + "ipaddr.js": "^1.9.1", + "is-ip": "^3.1.0", + "p-event": "^4.2.0" + } + }, + "ip-regex": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-4.3.0.tgz", + "integrity": "sha512-B9ZWJxHHOHUhUjCPrMpLD4xEq35bUTClHM1S6CBU5ixQnkZmwipwgc96vAd7AAGM9TGHvJR+Uss+/Ak6UphK+Q==" + }, "ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" }, + "is-ip": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-ip/-/is-ip-3.1.0.tgz", + "integrity": "sha512-35vd5necO7IitFPjd/YBeqwWnyDWbuLH9ZXQdMfDA8TEo7pv5X8yfrvVO3xbJbLUlERCMvf6X0hTUamQxCYJ9Q==", + "requires": { + "ip-regex": "^4.0.0" + } + }, + "is-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", + "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==" + }, "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" + }, "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -770,6 +848,11 @@ "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==", "optional": true }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" + }, "middie": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/middie/-/middie-5.2.0.tgz", @@ -793,6 +876,11 @@ "mime-db": "1.45.0" } }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" + }, "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", @@ -914,6 +1002,14 @@ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.20.tgz", "integrity": "sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw==" }, + "npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "requires": { + "path-key": "^3.0.0" + } + }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -922,11 +1018,45 @@ "wrappy": "1" } }, + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "p-event": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/p-event/-/p-event-4.2.0.tgz", + "integrity": "sha512-KXatOjCRXXkSePPb1Nbi0p0m+gQAwdlbhi4wQKJPI1HsMQS9g+Sqp2o+QHziPr7eYJyOZet836KoHEVM1mwOrQ==", + "requires": { + "p-timeout": "^3.1.0" + } + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" + }, + "p-timeout": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz", + "integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==", + "requires": { + "p-finally": "^1.0.0" + } + }, "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" + }, "path-to-regexp": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.2.0.tgz", @@ -1118,11 +1248,29 @@ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" + }, "sift": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/sift/-/sift-7.0.1.tgz", "integrity": "sha512-oqD7PMJ+uO6jV9EQCl0LrRw1OwsiPsiFQR5AR30heR+4Dl7jBBbDLnNvWiak20tzZlSE1H7RB30SX/1j/YYT7g==" }, + "signal-exit": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", + "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==" + }, "sliced": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/sliced/-/sliced-1.0.1.tgz", @@ -1203,6 +1351,11 @@ "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", "dev": true }, + "strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==" + }, "tiny-lru": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/tiny-lru/-/tiny-lru-7.0.6.tgz", @@ -1292,6 +1445,14 @@ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "requires": { + "isexe": "^2.0.0" + } + }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", diff --git a/package.json b/package.json index 1cf7db9..cee3518 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "fastify-jwt": "^2.2.0", "fastify-plugin": "^3.0.0", "fs-jetpack": "^4.1.0", + "internal-ip": "^6.2.0", "mime-types": "^2.1.28", "mongoose": "5.10.3", "mongoose-findorcreate": "^3.0.0", diff --git a/src/api.server.ts b/src/api.server.ts index 468a0cb..c55585e 100644 --- a/src/api.server.ts +++ b/src/api.server.ts @@ -6,57 +6,62 @@ import fastify, { } from 'fastify' import helmet from 'fastify-helmet' import { Server, IncomingMessage, ServerResponse } from 'http' -import {RouterMap} from './decorators/router'; -import {mongoose} from "@typegoose/typegoose"; -import logger from './logger/logger'; -import {Config} from "./cfg/Config"; -import {initData} from "./common/GConfig"; +import {RouterMap} from './decorators/router' +import {mongoose} from "@typegoose/typegoose" +import logger from './logger/logger' +import {Config} from "./cfg/Config" +import {initData} from "./common/GConfig" import { RedisClient } from './redis/RedisClient' import { initRobotCache } from './service/jcfw' import { error } from './common/Debug' +import { + registerGracefulShutdown, + registService, + unRegistService +} from './utils/system.util' -const zReqParserPlugin = require('./plugins/zReqParser'); -const apiAuthPlugin = require('./plugins/apiauth'); +const zReqParserPlugin = require('./plugins/zReqParser') +const apiAuthPlugin = require('./plugins/apiauth') -const fs = require('fs'); -const join = require('path').join; +const fs = require('fs') +const join = require('path').join -let config: Config = require('../config/config.json'); -require('./common/Extend'); +let config: Config = require('../config/config.json') +require('./common/Extend') export class ApiServer { - server: FastifyInstance; + server: FastifyInstance public constructor() { - this.server = fastify({logger: true}); - this.registerPlugins(); + this.server = fastify({logger: true}) + this.registerPlugins() } private registerPlugins() { - this.server.register(require('fastify-formbody')); - this.server.register(zReqParserPlugin); - this.server.register(apiAuthPlugin); + this.server.register(require('fastify-formbody')) + this.server.register(zReqParserPlugin) + this.server.register(apiAuthPlugin) this.server.register( helmet, { hidePoweredBy: false } ) - this.server.register(require('fastify-cors'), {}); + this.server.register(require('fastify-cors'), {}) } private registerRouter() { - logger.log('register api routers'); - let self = this; + logger.log('register api routers') + let self = this for (let [controller, config] of RouterMap.decoratedRouters) { for(let data of config.data) { logger.info('find api router', data.method || 'all', - data.path, controller.name); + data.path, controller.name) // @ts-ignore self.server[data.method || 'all'](data.path, { preValidation: async function (request: FastifyRequest, reply: FastifyReply) { - request.roles = config.roles; - await this.apiAuth(request, reply); + request.roles = config.roles + await this.apiAuth(request, reply) } - }, controller); + }, controller) } } } @@ -64,38 +69,38 @@ export class ApiServer { * 加载所有的controller */ initControllers() { - logger.info('Bootstrap controllers...'); - const controllers = join(__dirname, 'controllers'); + logger.info('Bootstrap controllers...') + const controllers = join(__dirname, 'controllers') fs.readdirSync(controllers) .filter((file: string) => ~file.search(/^[^.].*\.(ts|js)$/)) .forEach((file: any) => { - logger.log(file); - return require(join(controllers, file))}); + logger.log(file) + return require(join(controllers, file))}) } async connectDB() { const options = {useNewUrlParser: true, poolSize: 5, keepAlive: true, keepAliveInitialDelay: 300000, - useUnifiedTopology: true}; - const uri = config.db_main; - logger.info(`connect to ${uri} ...`); + useUnifiedTopology: true} + const uri = config.db_main + logger.info(`connect to ${uri} ...`) try { // await mongoose.createConnection(uri, options) await mongoose.connect(uri, options) - logger.log('DB Connected'); + logger.log('DB Connected') } catch (err) { - logger.log(`DB Connection Error: ${err.message}`); + logger.log(`DB Connection Error: ${err.message}`) } let opts = {url: config.redis} new RedisClient(opts) } private setErrHandler() { - this.server.setNotFoundHandler(function(request: any, reply: { send: (arg0: { errcode: number; errmsg: string; }) => void; }){ + this.server.setNotFoundHandler(function(request: any, reply: { send: (arg0: { errcode: number, errmsg: string }) => void }){ reply.send({errcode: 404, errmsg: 'page not found'}) - }); + }) this.server.setErrorHandler(function (error: FastifyError, request: FastifyRequest, reply: FastifyReply) { - let statusCode = error && error.statusCode || 100; + let statusCode = error && error.statusCode || 100 if (statusCode >= 500) { logger.error(error) } else if (statusCode >= 400) { @@ -136,24 +141,36 @@ export class ApiServer { error('error init robot info', err) } } + + /** + * 处理process意外退出 + * @param {boolean} exit + * @param {Error} err + * @return {Promise} + */ + public async gracefullyShutdown(exit: boolean = true, err?: Error) { + await unRegistService(config.port) + } public async start() { - let self = this; + let self = this return new Promise(async (resolve, reject) => { - await self.connectDB(); - await self.initCache(); - self.initControllers(); - self.registerRouter(); - self.setErrHandler(); - self.setFormatSend(); - initData(); + await registerGracefulShutdown((err) => this.gracefullyShutdown(true, err)); + await self.connectDB() + await self.initCache() + await registService(config.port) + self.initControllers() + self.registerRouter() + self.setErrHandler() + self.setFormatSend() + initData() this.server.listen({port: config.port}, (err: any, address: any) => { if (err) { logger.log(err) process.exit(0) } - resolve && resolve(address); + resolve && resolve(address) }) - }); + }) } diff --git a/src/utils/system.util.ts b/src/utils/system.util.ts new file mode 100644 index 0000000..8d5dcbb --- /dev/null +++ b/src/utils/system.util.ts @@ -0,0 +1,40 @@ +import { RedisClient } from '../redis/RedisClient' +import ip from 'internal-ip' + +const signals: NodeJS.Signals[] = ['SIGINT', 'SIGTERM', 'SIGUSR2'] + +const NODES_SET = 'poker:infosvr' +const discovery_channel = 'poker:infosvr:discovery' + +export function registerGracefulShutdown(callback: (err?: Error) => void) { + /** + * Gracefully shutdown on uncaught errors + */ + process.on('uncaughtException', (err) => { + console.error(err) + callback(err) + }) + + signals.forEach((signal) => { + process.once(signal, () => callback()) + }) +} + +async function getNodeAddress(port: number) { + const host = process.env.SELF_HOSTNAME || await ip.v4() + return `${ host }:${ port }` +} + +export async function registService(port: number) { + const address = await getNodeAddress(port) + const client = new RedisClient() + await client.sadd(NODES_SET, address) + await client.publish(discovery_channel, `add,${address}`) +} + +export async function unRegistService(port: number) { + const address = await getNodeAddress(port) + const client = new RedisClient() + await client.srem(NODES_SET, address) + await client.publish(discovery_channel, `remove,${address}`) +}