gpt4 book ai didi

node.js - 优雅启动 Node.js Web 服务器应用程序的好方法是什么

转载 作者:行者123 更新时间:2023-12-05 07:09:25 24 4
gpt4 key购买 nike

当我使用 express-generator 生成网络服务器时,我得到了这个文件夹结构:

  • bin/www
  • 浏览量/...
  • 应用程序.js
  • package.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、调度程序...)您的网络服务器应用程序已准备就绪。此信号可由进程管理或其他系统使用)

问题在于,在 bin/www 中,应用程序启动(调用 server.listen())时没有确保数据库连接已建立。换句话说,没有网络服务器应用准备好监听流量的保证。

我读到在 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

如果我启动我的应用程序,然后在 myWeatherService 初始化之前调用 localhost:port/getParisWeather,我会得到一个错误

我已经想到了一个解决方案:移动 bin/www 中的每个服务声明,并让 app.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

但我知道将逻辑放在 bin/www 中不是一个好习惯,它必须只包含服务器起始行...

所以,我的问题是,我们必须如何启动网络服务器应用程序才能尊重最佳实践//什么是最佳实践?

我知道我可以将所有内容都放在一个文件中并在该文件的末尾启动网络服务器,这不是我的问题。我问的是如何以好的方式和最佳实践去做

最佳答案

答案实际上取决于您的生态系统是什么样的。如果您知道您的应用程序将使用的所有服务,那么您可以尝试在路由代码之前调用的 expressjs 中间件函数中检查它们。您可以使用一组 promise 来跟踪服务就绪情况,并使用一个 bool 值来判断所有服务是否就绪。如果所有服务都准备就绪,那么中间件函数可以调用 next(),但如果没有,那么您可能会返回一个 HTML 页面,告诉用户站点正在进行维护或尚未准备好,他们应该稍后再试。我可以看到您将所有这些 promise 封装在一个中间件函数中,该函数管理它们是否准备好,以免弄乱您的 app.js 或 bin/www 文件。

更新:如果你想在服务准备好之前阻止服务器监听,那么你需要在同一进程中设置你自己的基础设施,或者使用类似 supervisord 的东西来管理进程。例如,您可以设置一个检查服务的“启动”进程。服务准备就绪后,您的启动进程可以 fork 并启动 Node 服务器或创建运行服务器的子进程。您不需要在您的 Node 应用程序中拥有任何服务检查逻辑;假设是如果它是由另一个进程启动的,那么服务已经启动并正在运行。可以引入supervisord这样的高级进程管理系统,也可以全部放在nodejs中,使用child_process模块​​。这种方法将有助于将“启动”代码与“运行/应用”代码分开。

关于node.js - 优雅启动 Node.js Web 服务器应用程序的好方法是什么,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61579096/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com