Как правильно запустить приложение для веб-сервера Node.js? - PullRequest
2 голосов
/ 03 мая 2020

Когда я генерирую веб-сервер с генератором express, я получаю следующую структуру папок:

  • bin / www*1004*
  • views /...
  • приложение. js
  • пакет. json
  • ...

bin/www вызов app.js вот так:

var app = require('../app');
// ...
var server = http.createServer(app);
server.listen(port);

app.js создайте приложение так:

var express = require('express')
var mongoose = require('mongoose')

mongoose.connect(process.env.DATABASE_URL).then(
    () => {
      debug('Database is connected')
    },
    err => {
      debug('An error has occured with the database connection')
      process.exit(1)
    }
  )

var app = express()

// Midllewares
app.use(/* some middleware 1 */)
app.use(/* some middleware 2 */)
app.use(/* some middleware 3 */)
app.use(/* some middleware ... */)

// Routes
app.get('/', function(req, res, next) {
  res.json({'message': 'Welcome to my website'})
})
app.get('/users', function(req, res, next) {
  Users.find({}).exec(function(err, users) {
    if (err) {
      res.json({'message': 'An error occured'})
      return
    }
    res.json('users': users)
  })
})
// ... others routes ...

module.exports = app

хорошо, это шаблон веб-сервера от express -generator. Но если я хочу запустить свое приложение хорошим способом, я должен позвонить process.send('ready'), когда мое приложение будет готово. («готово» означает, что все сервисы готовы к использованию: база данных, redis, планировщик ...) (звоните process.send('ready'), когда ваше приложение готово, рекомендуется знать, что ваше приложение веб-сервера готово. Этот сигнал можно использовать по управлению процессами или другой системой)

Проблема в том, что в bin/www приложение запускается (вызывается server.listen()) без гарантии того, что соединение с базой данных установлено. Другими словами, без страховки, что приложение веб-сервера готово к прослушиванию трафика c.

Я прочитал, что запуск сервера в bin/www - это лучшая практика

Выше пример не завершен, мы можем считать, что у нас есть приложение с несколькими службами, которое мы должны запустить, прежде чем принимать запросы (примеры служб: redis, планировщик заданий, соединение с базой данных, соединение ftp с другим сервером ...)

Я уже проверял некоторые популярные и продвинутые шаблоны приложения Node.js:

Но никто из них не заботится о состоянии готовности приложения перед вызовом server.listen(port), что заставляет веб-сервер начинать прослушивать входящие запросы. Это меня очень удивляет, и я не понимаю, почему

Пример кода приложения веб-сервера с несколькими службами, которые мы должны ждать, прежде чем принимать запросы на поступление:

bin/www:

var app = require('../app');
// ...
var server = http.createServer(app);
server.listen(port);

app.js:

var express = require('express')
var mongoose = require('mongoose')

// **************
// Service 1 : database
mongoose.connect(process.env.DATABASE_URL).then(
  () => {
    debug('Database is connected')
  },
  err => {
    debug('An error has occured with the database connection')
    process.exit(1)
  }
)
// **************

// **************
// Service 2
// Simulate a service that take 10 seconds to initialized
var myWeatherService = null
setTimeout(function() {
  myWeatherService.getWeatherForTown = function(town, callback) {
    weather = 'sun'
    callback(null, weather)
  }
}, 10*1000)
// **************

// **************
// Other services...
// **************

var app = express()

// Midllewares
app.use(/* some middleware 1 */)
app.use(/* some middleware 2 */)
app.use(/* some middleware 3 */)
app.use(/* some middleware ... */)

// Routes
app.get('/', function(req, res, next) {
  res.json({'message': 'Welcome to my website'})
})
app.get('/users', function(req, res, next) {
  Users.find({}).exec(function(err, users) {
    if (err) {
      res.json({'message': 'An error occured'})
      return
    }
    res.json({'users': users})
  })
})
app.get('/getParisWeather', function(req, res, next) {
  Users.getWeatherForTown('Paris', function(err, weather) {
    if (err) {
      res.json({'message': 'An error occured'})
      return
    }
    res.json({'town': 'Paris', weatcher: weather})
  })
})
// ... others routes ...

module.exports = app

Если я запускаю свое приложение, а затем звоню localhost:port/getParisWeather до инициализации myWeatherService, я получу ошибку

Я уже думаю о решении: переместить каждую декларацию сервиса в bin / www and впустить приложение. js только код, относящийся к декларации express app:

bin/www:

var app = require('../app');
var mongoose = require('mongoose')
var server = null;

Promise.resolve()
.then(function () {
  return new Promise(function (resolve, reject) {
    // start service 1
    console.log('Service 1 is ready')
    resolve()
  })
})
.then(function () {
  return new Promise(function (resolve, reject) {
    // start service 2
    console.log('Service 2 is ready')
    resolve()
  })
})
.then(function () {
  return new Promise(function (resolve, reject) {
    // start other services...
    console.log('Others services is ready')
    resolve()
  })
})
.then(function () {
  return new Promise(function (resolve, reject) {
    server = http.createServer(app);
    server.listen(port);
    console.log('Server start listenning')
  })
})
.then(function () {
  next()
})
.catch(next)
.finally(function () {

})
.done()

app.js:

var express = require('express')
var app = express()

// Midllewares
app.use(/* some middleware 1 */)
app.use(/* some middleware 2 */)
app.use(/* some middleware 3 */)
app.use(/* some middleware ... */)

// Routes
app.get('/', function(req, res, next) {
  res.json({'message': 'Welcome to my website'})
})
app.get('/users', function(req, res, next) {
  Users.find({}).exec(function(err, users) {
    if (err) {
      res.json({'message': 'An error occured'})
      return
    }
    res.json({'users': users})
  })
})
app.get('/getParisWeather', function(req, res, next) {
  Users.getWeatherForTown('Paris', function(err, weather) {
    if (err) {
      res.json({'message': 'An error occured'})
      return
    }
    res.json({'town': 'Paris', weatcher: weather})
  })
})
// ... others routes ...

module.exports = app

Но я знаю, что помещать logi c в bin / www is не очень хорошая практика, он должен содержать только строки запуска сервера ...

Итак, мой вопрос, как мы должны запустить приложение веб-сервера, чтобы уважать практики лучших, // что такое практики лучших?

Я знаю что я могу поместить все в один файл и запустить веб-сервер в конце этого файла, это не мой вопрос. Я спрашиваю, как это сделать наилучшим образом и в соответствии с лучшими практиками

Ответы [ 2 ]

0 голосов
/ 05 мая 2020

Рассмотрим простой express API, который возвращает «OK» на порт 3000.

const express = require('express');
const app = express();

app.get('/', (req, res) => {
    res.send('OK');
});

app.listen(3000, () => {
    console.log("App listening on port 3000");
});

Он готов к приему подключений, как только приложение запустится. Теперь давайте поспим 5 секунд, чтобы подделать базу данных и подготовить ее, а затем вручную запустить событие «Ready». Мы начинаем прослушивать соединения, когда событие перехватывается.

const express = require('express');
const sleep = time => new Promise(resolve => setTimeout(resolve, time));

const app = express();

app.get('/', (req, res) => {
    res.send('OK');
});

sleep(5000)
.then(() => {
    process.emit("ready");
});

process.on("ready", () => {
    app.listen(3000, () => {
        console.log("App listening on port 3000");
    });
});

Проверьте, перейдя к http://localhost: 3000 . Вы увидите «OK» только через 5 секунд.

Здесь вам не нужно отправлять событие «ready» на сам объект process. Пользовательский объект EventEmitter также будет выполнять эту работу. Объект process наследуется от EventEmitter и доступен глобально. Так что это удобный способ прослушивания любых глобальных событий.

0 голосов
/ 03 мая 2020

Ответ действительно зависит от того, на что похожа ваша экосистема. Если вы знаете все службы, которые будет использовать ваше приложение, вы можете попробовать проверить их в функции промежуточного программного обеспечения expressjs, которая вызывается перед кодом маршрутизации. Вы можете использовать набор обещаний для отслеживания готовности службы и логическое значение, чтобы сказать, готовы ли все службы. Если все службы готовы, то функция промежуточного программного обеспечения может вызвать next(), но если нет, то вы можете вернуть страницу HTML, которая сообщает пользователю, что сайт находится на обслуживании или не готов, и они должны повторить попытку позже. Я вижу, как вы инкапсулируете все эти обещания в функцию промежуточного программного обеспечения, которая определяет, готовы ли они или нет, чтобы не загромождать ваше приложение. js или bin / www files.

Обновление: если вы хотите предотвратить от прослушивания сервера до тех пор, пока сервисы не будут готовы, тогда вам нужно будет настроить собственную инфраструктуру в том же процессе или использовать что-то вроде supervisord для управления процессами. Например, вы можете настроить процесс запуска, который проверяет сервисы. Как только службы будут готовы, ваш процесс запуска может разветвиться и запустить сервер узла или создать дочерний процесс, который запускает сервер. Вам не нужно иметь какой-либо сервис, проверяющий логи c в вашем приложении узла; Предполагается, что если он запущен другим процессом, то службы уже запущены и работают. Вы можете внедрить систему управления процессами высокого уровня, такую ​​как supervisord, или оставить все это в nodejs и использовать модуль child_process. Этот подход поможет отделить код запуска от кода запуска / приложения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...