0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-02-10 23:36:14 -05:00

Improved ghost-server with async/await

- Using consistent patterns for shutdown and stop
- Make it clear what each method does
- Use async/await to make the code more readable and simple
- This lays groundwork for having more cleanup tasks in stop than just server.stop()
This commit is contained in:
Hannah Wolfe 2020-08-11 13:28:04 +01:00
parent 1f69b22b30
commit 624206b6d7

View file

@ -14,7 +14,6 @@ const logging = require('../shared/logging');
const moment = require('moment'); const moment = require('moment');
const bootstrapSocket = require('@tryghost/bootstrap-socket'); const bootstrapSocket = require('@tryghost/bootstrap-socket');
const stoppable = require('stoppable'); const stoppable = require('stoppable');
const {reject} = require('bluebird');
/** /**
* ## GhostServer * ## GhostServer
@ -103,7 +102,7 @@ class GhostServer {
self.httpServer.on('listening', function () { self.httpServer.on('listening', function () {
debug('...Started'); debug('...Started');
self.logStartMessages(); self._logStartMessages();
return GhostServer.announceServerReadiness() return GhostServer.announceServerReadiness()
.finally(() => { .finally(() => {
@ -113,21 +112,10 @@ class GhostServer {
stoppable(self.httpServer, config.get('server:shutdownTimeout')); stoppable(self.httpServer, config.get('server:shutdownTimeout'));
async function shutdown() {
try {
logging.warn(i18n.t('notices.httpServer.ghostIsShuttingDown'));
await self.stop();
process.exit(0);
} catch (error) {
logging.error(error);
process.exit(-1);
}
}
// ensure that Ghost exits correctly on Ctrl+C and SIGTERM // ensure that Ghost exits correctly on Ctrl+C and SIGTERM
process process
.removeAllListeners('SIGINT').on('SIGINT', shutdown) .removeAllListeners('SIGINT').on('SIGINT', self.shutdown.bind(self))
.removeAllListeners('SIGTERM').on('SIGTERM', shutdown); .removeAllListeners('SIGTERM').on('SIGTERM', self.shutdown.bind(self));
if (config.get('server:testmode')) { if (config.get('server:testmode')) {
// Debug code // Debug code
@ -138,48 +126,68 @@ class GhostServer {
}); });
} }
/**
* ### Shutdown
* Stops the server, handles cleanup and exits the process = a full shutdown
* Called on SIGINT or SIGTERM
*/
async shutdown() {
try {
logging.warn(i18n.t('notices.httpServer.ghostIsShuttingDown'));
await this.stop();
process.exit(0);
} catch (error) {
logging.error(error);
process.exit(-1);
}
}
/** /**
* ### Stop * ### Stop
* Returns a promise that will be fulfilled when the server stops. If the server has not been started, * Stops the server & handles cleanup, but does not exit the process
* the promise will be fulfilled immediately * Used in tests for quick start/stop actions
* Called by shutdown to handle server stop and cleanup before exiting
* @returns {Promise} Resolves once Ghost has stopped * @returns {Promise} Resolves once Ghost has stopped
*/ */
stop() { async stop() {
const self = this; if (this.httpServer === null) {
return;
}
return new Promise(function (resolve) { await this._stopServer();
if (self.httpServer === null) { events.emit('server.stop');
resolve(self); this.httpServer = null;
} else { this._logStopMessages();
// The stop function comes from stoppable
self.httpServer.stop(function (err) {
if (err) {
reject(self);
}
events.emit('server.stop');
self.httpServer = null;
self.logStopMessages();
resolve(self);
});
}
});
} }
/** /**
* ### Hammertime * ### Hammertime
* To be called after `stop` * To be called after `stop`
*/ */
hammertime() { async hammertime() {
logging.info(i18n.t('notices.httpServer.cantTouchThis')); logging.info(i18n.t('notices.httpServer.cantTouchThis'));
}
return Promise.resolve(this); /**
* ### Stop Server
* Does the work of stopping the server using stoppable
* This handles closing connections:
* - New connections are rejected
* - Idle connections are closed immediately
* - Active connections are allowed to complete in-flight requests before being closed
*
* If server.shutdownTimeout is reached, requests are terminated in-flight
*/
async _stopServer() {
return new Promise((resolve, reject) => {
this.httpServer.stop((err, status) => (err ? reject(err) : resolve(status)));
});
} }
/** /**
* ### Log Start Messages * ### Log Start Messages
*/ */
logStartMessages() { _logStartMessages() {
logging.info(i18n.t('notices.httpServer.ghostIsRunningIn', {env: config.get('env')})); logging.info(i18n.t('notices.httpServer.ghostIsRunningIn', {env: config.get('env')}));
if (config.get('env') === 'production') { if (config.get('env') === 'production') {
@ -197,8 +205,9 @@ class GhostServer {
/** /**
* ### Log Stop Messages * ### Log Stop Messages
* Private / internal API
*/ */
logStopMessages() { _logStopMessages() {
logging.warn(i18n.t('notices.httpServer.ghostHasShutdown')); logging.warn(i18n.t('notices.httpServer.ghostHasShutdown'));
// Extra clear message for production mode // Extra clear message for production mode