【发布时间】:2020-05-05 18:58:48
【问题描述】:
当我使用 express-generator 生成网络服务器时,我得到了这个文件夹结构:
- bin/www
- 观看次数/...
- app.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、调度程序...)(当您的应用程序准备好时调用process.send('ready') 是了解您的网络服务器应用程序已准备好的最佳实践。可以使用此信号通过进程管理或其他系统)
问题是在bin/www 中,应用程序在没有建立数据库连接的情况下启动(调用server.listen())。换句话说,没有网络服务器应用准备好监听流量的保险。
我读到在bin/www 中启动服务器是一种最佳做法
上面的例子并不完整,我们可以认为我们有一个应用程序在接受请求之前必须启动多个服务(服务示例:redis、作业调度程序、数据库连接、ftp 连接到另一个服务器...)
我已经查看了一些流行和高级的 Node.js 应用样板:
- https://github.com/sahat/hackathon-starter
- https://github.com/kriasoft/nodejs-api-starter
- https://github.com/madhums/node-express-mongoose
- https://github.com/icebob/vue-express-mongo-boilerplate
- https://github.com/talyssonoc/node-api-boilerplate
但在调用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 不是一个好习惯,它必须只包含服务器起始行...
所以,我的问题是,我们必须如何启动网络服务器应用程序才能尊重最佳实践 // 最佳实践是什么?
我知道我可以将所有内容放在一个文件中,并在该文件的末尾启动网络服务器,这不是我的问题。我要问的是如何以好的方式和最佳实践来做到这一点
【问题讨论】:
-
如果你有一个'ready'事件,为什么你不能在所有服务都准备好的时候做
process.emit('ready')并通过监听像process.on('ready', () => app.listen(port))这样的事件来启动服务器 -
从应用程序发出“就绪”事件并从应用程序监听“就绪”事件以在应用程序中发生某些事情是一种好习惯吗?我认为让应用程序自己监听以启动某些东西(在我们的例子中启动 server.listen() )在我看来不是一个好习惯!?编辑:我没有想到的另一件事:“就绪”事件是告诉应用程序已经准备好监听请求。如果你做
process.on(ready, app.listen(port)),在你发出“ready”的那一刻,应用程序还没有准备好监听请求 -
查看答案