Browse Source

impl graceful shutdown with terminus and express-graceful-exit

Yuki Takei 4 years ago
parent
commit
7246df4f76
3 changed files with 59 additions and 2 deletions
  1. 2 0
      packages/slackbot-proxy/package.json
  2. 33 2
      packages/slackbot-proxy/src/Server.ts
  3. 24 0
      yarn.lock

+ 2 - 0
packages/slackbot-proxy/package.json

@@ -18,6 +18,7 @@
     "version": "node -p \"require('./package.json').version\""
   },
   "dependencies": {
+    "@godaddy/terminus": "^4.8.0",
     "@growi/slack": "^0.9.0-RC",
     "@slack/oauth": "^2.0.1",
     "@slack/web-api": "^6.1.0",
@@ -34,6 +35,7 @@
     "cross-env": "^7.0.0",
     "dotenv-flow": "^3.2.0",
     "express-bunyan-logger": "^1.3.3",
+    "express-graceful-exit": "^0.5.2",
     "helmet": "^4.6.0",
     "method-override": "^3.0.0",
     "mysql2": "^2.2.5",

+ 33 - 2
packages/slackbot-proxy/src/Server.ts

@@ -1,5 +1,5 @@
 import { Configuration, Inject, InjectorService } from '@tsed/di';
-import { PlatformApplication } from '@tsed/common';
+import { HttpServer, PlatformApplication } from '@tsed/common';
 import '@tsed/platform-express'; // !! DO NOT MODIFY !!
 import '@tsed/typeorm'; // !! DO NOT MODIFY !! -- https://github.com/tsedio/tsed/issues/1332#issuecomment-837840612
 import '@tsed/swagger';
@@ -9,9 +9,12 @@ import compress from 'compression';
 import cookieParser from 'cookie-parser';
 import methodOverride from 'method-override';
 import helmet from 'helmet';
+import { Express } from 'express';
 import expressBunyanLogger from 'express-bunyan-logger';
+import gracefulExit from 'express-graceful-exit';
 
 import { ConnectionOptions } from 'typeorm';
+import { createTerminus } from '@godaddy/terminus';
 
 import swaggerSettingsForDev from '~/config/swagger/config.dev';
 import swaggerSettingsForProd from '~/config/swagger/config.prod';
@@ -20,6 +23,9 @@ import loggerFactory from '~/utils/logger';
 export const rootDir = __dirname;
 const isProduction = process.env.NODE_ENV === 'production';
 
+const logger = loggerFactory('slackbot-proxy:server');
+
+
 const connectionOptions: ConnectionOptions = {
   // The 'name' property must be set. Otherwise, the 'name' will be '0' and won't work well. -- 2021.04.05 Yuki Takei
   // see: https://github.com/TypedProject/tsed/blob/7630cda20a1f6fa3a692ecc3e6cd51d37bc3c45f/packages/typeorm/src/utils/createConnection.ts#L10
@@ -86,7 +92,7 @@ const helmetOptions = isProduction ? {} : {
 export class Server {
 
   @Inject()
-  app: PlatformApplication;
+  app: PlatformApplication<Express>;
 
   @Configuration()
   settings: Configuration;
@@ -100,10 +106,19 @@ export class Server {
     if (serverUri === undefined) {
       throw new Error('The environment variable \'SERVER_URI\' must be defined.');
     }
+
+    const server = this.injector.get<HttpServer>(HttpServer);
+
+    // init express-graceful-exit
+    // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+    gracefulExit.init(server!);
   }
 
   $beforeRoutesInit(): void {
+    const expressApp = this.app.getApp();
+
     this.app
+      .use(gracefulExit.middleware(expressApp))
       .use(cookieParser())
       .use(compress({}))
       .use(methodOverride())
@@ -115,6 +130,22 @@ export class Server {
     this.setupLogger();
   }
 
+  $beforeListen(): void {
+    const expressApp = this.app.getApp();
+    const server = this.injector.get<HttpServer>(HttpServer);
+
+    // init terminus
+    createTerminus(server, {
+      onSignal: async() => {
+        logger.info('server is starting cleanup');
+        // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+        gracefulExit.gracefulExitHandler(expressApp, server!);
+      },
+      onShutdown: async() => {
+        logger.info('cleanup finished, server is shutting down');
+      },
+    });
+  }
 
   /**
    * Setup logger for requests

+ 24 - 0
yarn.lock

@@ -1433,6 +1433,13 @@
   resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.2.5.tgz#8eed982e2ee6f7f4e44c253e12962980791efd46"
   integrity sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA==
 
+"@godaddy/terminus@^4.8.0":
+  version "4.8.0"
+  resolved "https://registry.yarnpkg.com/@godaddy/terminus/-/terminus-4.8.0.tgz#7f1258abd731adcf5f08d8ff1aa0216a02d65062"
+  integrity sha512-C3u+LTmlhtqsk1Sjw9UDTAXfVngby62dJL71LyhpRcwX3FDeLb0yd1Rmxh1OjH5HouOr0IIuU4dhiJMT6NNXog==
+  dependencies:
+    stoppable "^1.1.0"
+
 "@google-cloud/common@^3.6.0":
   version "3.6.0"
   resolved "https://registry.yarnpkg.com/@google-cloud/common/-/common-3.6.0.tgz#c2f6da5f79279a4a9ac7c71fc02d582beab98e8b"
@@ -7871,6 +7878,13 @@ express-form@~0.12.0:
     object-additions "^0.5.1"
     validator "^2.1.0"
 
+express-graceful-exit@^0.5.2:
+  version "0.5.2"
+  resolved "https://registry.yarnpkg.com/express-graceful-exit/-/express-graceful-exit-0.5.2.tgz#5623f0d8dfd2c8e0011e1c6d12349987adbe6bff"
+  integrity sha512-qBk0zo/2MMFJiodZAfaAMXqaNTVb83/893m34gOCuoySIrzbh0eReBNYXe2LJ7qDJI/JMCnPvvMYeq+Q0ncYJQ==
+  dependencies:
+    underscore "^1.12.1"
+
 express-session@^1.16.1:
   version "1.16.1"
   resolved "https://registry.yarnpkg.com/express-session/-/express-session-1.16.1.tgz#251ff9776c59382301de6c8c33411af357ed439c"
@@ -17405,6 +17419,11 @@ sticky-events@^3.1.3:
   resolved "https://registry.yarnpkg.com/sticky-events/-/sticky-events-3.1.3.tgz#7b6b4091988b87b9f4e711c7c6532de07ab156dd"
   integrity sha512-nTm2bDaYTXFHAyQS59mWDRnnno/D8oj3C4JddOdipq6ZRnLLqjj+PeyCSbHPwMVdfvQoKwmMmAztp+YybDhvtA==
 
+stoppable@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/stoppable/-/stoppable-1.1.0.tgz#32da568e83ea488b08e4d7ea2c3bcc9d75015d5b"
+  integrity sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw==
+
 stream-browserify@^2.0.1:
   version "2.0.1"
   resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.1.tgz#66266ee5f9bdb9940a4e4514cafb43bb71e5c9db"
@@ -18739,6 +18758,11 @@ umask@^1.1.0:
   resolved "https://registry.yarnpkg.com/umask/-/umask-1.1.0.tgz#f29cebf01df517912bb58ff9c4e50fde8e33320d"
   integrity sha1-8pzr8B31F5ErtY/5xOUP3o4zMg0=
 
+underscore@^1.12.1:
+  version "1.13.1"
+  resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.13.1.tgz#0c1c6bd2df54b6b69f2314066d65b6cde6fcf9d1"
+  integrity sha512-hzSoAVtJF+3ZtiFX0VgfFPHEDRm7Y/QPjGyNo4TVdnDTdft3tr8hEkD25a1jC+TjTuE7tkHGKkhwCgs9dgBB2g==
+
 unherit@^1.0.4:
   version "1.1.2"
   resolved "https://registry.yarnpkg.com/unherit/-/unherit-1.1.2.tgz#14f1f397253ee4ec95cec167762e77df83678449"