在没有 Express 的情况下正确搭建 Node.js 应用程序(该应用程序不接收请求)
Scaffolding a Node.js app properly without Express (the app doesn't receive requests)
[这个问题比较含糊,请见谅。我正在尝试通过自己回答问题来解决我的各种问题]
我正在构建一个 Node.js 应用程序,它必须按给定的时间间隔执行各种任务。这是全局脚手架(涉及用于 DB 交互的 bluebird promises 和 mongoose):
var Promise = require("bluebird");
var mongoose = require('mongoose');
mongoose.Promise = require('bluebird');
// Personal modules
var bootApp = require(...);
var doStuffA = require(...);
var doStuffB = require(...);
var doStuffC = require(...);
// running locally, but meant to be deployed at some point
mongoose.connect('mongodb://localhost:27017/myDatabase');
var db = mongoose.connection;
db.on('error', () => {
console.log("Error : lost connection !"));
process.exit(1);
});
db.once('open', () => {
bootApp() // always start by booting
.then( () => { // then start the infinite loop of events
setInterval(doStuffA, 1000*60*60); // 1x/1h
setInterval(doStuffB, 1000*60*10); // 1x/10min
setInterval(doStuffC, 1000*60*3); // 1x/3min
}).catch((e) => { // errors are handled by doStuffX(), so we should never catch anything here
console.log(e.message);
process.exit(1);
});
});
每个模块 doStuffX
都是一个函数,返回一个 Promise,处理自己的错误,并且应该在某个时候完成。
整个应用的预期行为:
- 该应用程序应该能够 运行 永远
- 无论上次是成功还是失败,应用程序都应该在给定的时间间隔内尝试
doStuffX()
。
- [可选 :] 应用程序应在收到 "shut down" 信号后顺利关闭而无需重试
doStuff
。
我的问题:如何为这样的应用构建干净的脚手架?我可以摆脱 setInterval
并改用 promises 吗?我主要关心的问题之一是确保 doStuffX()
的前一个实例在开始下一个实例之前完成,即使它以某种方式涉及 "killing"。
我对任何 link 脚手架应用持开放态度,但请不要给我 ANSWER/LINK 涉及 EXPRESS :我不需要 Express,因为我的应用没有收到任何请求. (到目前为止,我发现的所有内容都以 Express 开头:/)
[我回答了我自己的问题,试图把我之后更改的所有内容都放在这里,以防有一天有人掉进这个页面...]
对于脚手架的 Mongoose 部分,这是我目前获得的可靠的长期数据库连接:
- Mongoose documentation 提供了一种奇特的方式来确保驱动程序永远不会放弃尝试与
reconnectTries
重新连接
- 我不太明白
socketOptions
和 keepalive
似乎与副本有关,所以我暂时将它们从我的代码中删除
- 由于 Mongoose 应该在出现问题时自动重新连接,因此我将保留
db.once('open')
作为对应用程序代码本身的访问权限,尽管我还不太了解 db.on('connected')
的区别
- 我推荐阅读this。
var Promise = require("bluebird");
var mongoose = require('mongoose');
mongoose.Promise = require('bluebird');
// Personal modules
var bootApp = require(...);
var doStuffA = require(...);
var doStuffB = require(...);
var doStuffC = require(...);
// running locally, but meant to be deployed at some point
var uri = 'mongodb://localhost:27017/myDatabase';
// the added option makes sure the app will always try to reconnect...
mongoose.connect(uri, { server: { reconnectTries: Number.MAX_VALUE } });
var db = mongoose.connection;
db.on('error', () => {
console.log("Error with Mongoose connection."));
});
db.once('open', () => {
bootApp() // always start by booting
.then( () => { // then start the infinite loop of events
//////////////////////////////////
/// Here goes the actual stuff ///
//////////////////////////////////
}).catch((e) => { // errors are handled by doStuffX(), so we should never catch anything here
console.log(e.message);
});
});
现在,对于实际的重复性内容,我的 objective 是确保一切顺利进行,并且没有进程卡住。关于我所做的更改:
- 使用的方法不是原生 ES6,而是 bluebird 特有的。您可以阅读 .timeout() and .delay(),我发现它对于在干净的代码中链接超时和间隔非常有用。
- 在我看来,
.then(runA, runA)
应该总是启动一个 runA
的唯一实例,但我担心我是否真的可以最终启动两个并行实例...
// Instead of using setInterval in a bluebird promised environment...
setInterval(doStuffA, 1000*60*60); // 1x/1h
// I would have liked a full promise chain, but as jfriend00 stated,
// It will end up crashing because the initial promise is never resolved...
function runA() {
return doStuffA()
.timeout(1000*60*30) // kill the running instance if it takes longer than 30min
.delay(1000*60*60) // wait 60min
.then(runA, runA); // whatever the outcome, restart the process
}
runA();
// Therefore, a solution like jfriend00's seems like the way to go :
function runA() {
setTimeout(function() {
doStuffA()
.timeout(1000*60*30)
.then(runA, runA)
}, 1000*60*60);
}
runA();
如果您不想在上一个完成之前开始下一个 doStuffX()
,那么您可以用重复的 setTimeout()
调用替换您的 setInterval()
。
function runA() {
setTimeout(function() {
doStuffA().then(runA).catch(function(err) {
// decide what to do differently if doStuffA has an error
});
}, 1000*60*60);
}
runA();
您还可以为此添加超时,这样如果 doStuffA()
在一定时间内没有响应,您就可以采取其他措施。这将涉及使用另一个计时器和超时标志。
[这个问题比较含糊,请见谅。我正在尝试通过自己回答问题来解决我的各种问题]
我正在构建一个 Node.js 应用程序,它必须按给定的时间间隔执行各种任务。这是全局脚手架(涉及用于 DB 交互的 bluebird promises 和 mongoose):
var Promise = require("bluebird");
var mongoose = require('mongoose');
mongoose.Promise = require('bluebird');
// Personal modules
var bootApp = require(...);
var doStuffA = require(...);
var doStuffB = require(...);
var doStuffC = require(...);
// running locally, but meant to be deployed at some point
mongoose.connect('mongodb://localhost:27017/myDatabase');
var db = mongoose.connection;
db.on('error', () => {
console.log("Error : lost connection !"));
process.exit(1);
});
db.once('open', () => {
bootApp() // always start by booting
.then( () => { // then start the infinite loop of events
setInterval(doStuffA, 1000*60*60); // 1x/1h
setInterval(doStuffB, 1000*60*10); // 1x/10min
setInterval(doStuffC, 1000*60*3); // 1x/3min
}).catch((e) => { // errors are handled by doStuffX(), so we should never catch anything here
console.log(e.message);
process.exit(1);
});
});
每个模块 doStuffX
都是一个函数,返回一个 Promise,处理自己的错误,并且应该在某个时候完成。
整个应用的预期行为:
- 该应用程序应该能够 运行 永远
- 无论上次是成功还是失败,应用程序都应该在给定的时间间隔内尝试
doStuffX()
。 - [可选 :] 应用程序应在收到 "shut down" 信号后顺利关闭而无需重试
doStuff
。
我的问题:如何为这样的应用构建干净的脚手架?我可以摆脱 setInterval
并改用 promises 吗?我主要关心的问题之一是确保 doStuffX()
的前一个实例在开始下一个实例之前完成,即使它以某种方式涉及 "killing"。
我对任何 link 脚手架应用持开放态度,但请不要给我 ANSWER/LINK 涉及 EXPRESS :我不需要 Express,因为我的应用没有收到任何请求. (到目前为止,我发现的所有内容都以 Express 开头:/)
[我回答了我自己的问题,试图把我之后更改的所有内容都放在这里,以防有一天有人掉进这个页面...]
对于脚手架的 Mongoose 部分,这是我目前获得的可靠的长期数据库连接:
- Mongoose documentation 提供了一种奇特的方式来确保驱动程序永远不会放弃尝试与
reconnectTries
重新连接
- 我不太明白
socketOptions
和keepalive
似乎与副本有关,所以我暂时将它们从我的代码中删除 - 由于 Mongoose 应该在出现问题时自动重新连接,因此我将保留
db.once('open')
作为对应用程序代码本身的访问权限,尽管我还不太了解db.on('connected')
的区别 - 我推荐阅读this。
var Promise = require("bluebird");
var mongoose = require('mongoose');
mongoose.Promise = require('bluebird');
// Personal modules
var bootApp = require(...);
var doStuffA = require(...);
var doStuffB = require(...);
var doStuffC = require(...);
// running locally, but meant to be deployed at some point
var uri = 'mongodb://localhost:27017/myDatabase';
// the added option makes sure the app will always try to reconnect...
mongoose.connect(uri, { server: { reconnectTries: Number.MAX_VALUE } });
var db = mongoose.connection;
db.on('error', () => {
console.log("Error with Mongoose connection."));
});
db.once('open', () => {
bootApp() // always start by booting
.then( () => { // then start the infinite loop of events
//////////////////////////////////
/// Here goes the actual stuff ///
//////////////////////////////////
}).catch((e) => { // errors are handled by doStuffX(), so we should never catch anything here
console.log(e.message);
});
});
现在,对于实际的重复性内容,我的 objective 是确保一切顺利进行,并且没有进程卡住。关于我所做的更改:
- 使用的方法不是原生 ES6,而是 bluebird 特有的。您可以阅读 .timeout() and .delay(),我发现它对于在干净的代码中链接超时和间隔非常有用。
- 在我看来,
.then(runA, runA)
应该总是启动一个runA
的唯一实例,但我担心我是否真的可以最终启动两个并行实例...
// Instead of using setInterval in a bluebird promised environment...
setInterval(doStuffA, 1000*60*60); // 1x/1h
// I would have liked a full promise chain, but as jfriend00 stated,
// It will end up crashing because the initial promise is never resolved...
function runA() {
return doStuffA()
.timeout(1000*60*30) // kill the running instance if it takes longer than 30min
.delay(1000*60*60) // wait 60min
.then(runA, runA); // whatever the outcome, restart the process
}
runA();
// Therefore, a solution like jfriend00's seems like the way to go :
function runA() {
setTimeout(function() {
doStuffA()
.timeout(1000*60*30)
.then(runA, runA)
}, 1000*60*60);
}
runA();
如果您不想在上一个完成之前开始下一个 doStuffX()
,那么您可以用重复的 setTimeout()
调用替换您的 setInterval()
。
function runA() {
setTimeout(function() {
doStuffA().then(runA).catch(function(err) {
// decide what to do differently if doStuffA has an error
});
}, 1000*60*60);
}
runA();
您还可以为此添加超时,这样如果 doStuffA()
在一定时间内没有响应,您就可以采取其他措施。这将涉及使用另一个计时器和超时标志。