在哪里存储我的节点计划
Posted
技术标签:
【中文标题】在哪里存储我的节点计划【英文标题】:Where to store my node-schedules 【发布时间】:2015-08-18 07:54:47 【问题描述】:我是 Node/Express 的新手,我正在做一个预约系统。我希望我的用户在他们想要的那一天进行预约,我的系统会在那个确切的时间向他们发送通知。我发现“node-schedule”模块非常适合这项任务,但我不知道在哪里实现它。无论如何都可以将所有任务存储在我的 app.js 中,或者每次我达到某个终点时只创建一个节点计划任务就足够了:
router.get('/', function(req, res, next)
var j = schedule.scheduleJob(date, function()
send notification();
);
res.send(200);
注意:我不想在我的 sql 表上运行一个常量 for 循环来检查日期
【问题讨论】:
【参考方案1】:您需要通过使用SQLite 之类的方式写入本地文件、运行您自己的数据库服务器(例如MongoDB)或使用基于云的存储服务,将您的应用程序数据持久化到某种形式的永久存储中喜欢Amazon SimpleDb。
这些选项中的每一个(以及许多其他选项)都具有可用于读取/写入/删除持久数据的 npm 模块。例如,请参阅 MongoDb、SQLite3 和 SimpleDb,所有这些都可以在 npmjs.com 上使用 npm
获得。
更新
根据您在下面的评论:嗯,您确实询问了可以将预定事件存储在哪里。 ;)
要保留所有计划的事件,以便它们在可能的服务器故障中幸存下来,您需要创建一个可存储的数据结构来表示它们,并为每个事件创建一个新的表示实例并将其存储到您的持久存储 (mysql) .
通常,您会使用以下内容:
when:DateTime -- timestamp when the event should fire
what:Action -- what this event should do
args:Arguments -- arguments to pass to Action
pending:Boolean=true -- if false, this event has already fired
当你初始化你的服务器时,你会在你的持久存储中查询pending===true
的所有事件,并使用结果初始化node-schedule
模块的实例。
当您需要在服务器运行时安排一个新事件时,您需要创建一个新的事件表示,将其写入持久存储并使用它创建一个 node-schedule
的新实例。
最后,最重要的是为了客户的幸福,当计划的事件成功完成时,就在您的事件处理程序(上面提到的Action
)完成之前,它需要标记持久版本它以pending:false
处理的事件,因此您不会多次触发任何事件。
例如:
'use strict';
var scheduler = require('node-schedule');
/**
* Storable Representation of a Scheduled Event
*
* @param string|Date when
* @param string what
* @param array.<string> [args=[]]
* @param boolean [pending=true]
*
* @property Date PersistentEvent.when - the datetime this event should fire.
* @property string PersistentEvent.what - the name of the action to run (must match key of PersistentEvent.Actions)
* @property array PersistentEvent.args - args to pass to action event handler.
* @property boolean PersistentEvent.pending - if true, this event has not yet fired.
*
* @constructor
*
* @example
*
* var PersistentEvent = require('PersistentEvent'),
* mysql = require('mysql'),
* conn = mysql.createConnection( ... );
*
* conn.connect();
*
* // at some point when initializing your app...
*
* // assign your persistent storage connection...
* PersistentEvent.setStore(conn);
*
* // load all pending event from persistent storage...
* PersistentEvent.loadAll$(function (err)
* if (err)
* throw new Error('failed to load all PersistentEvents: ' + err);
*
*
* // from this point on, all persistent events are loaded and running.
*
* );
*/
var PersistentEvent = function (when, what, args, pending)
// initialize
PersistentEvent.Cache.push(this.init(
when: when,
what: what,
args: args,
pending: pending
));
;
// ==== PersistentEvent Static Methods ====
/**
* Pre-defined action event handlers.
* <p>
* Where the property key will be used to match the PersistentEvent.what property,
* and the property value is a event handler function that accepts an optional
* array of args and a callback (provided by PersistentEvent.prototype.schedule)
* </p>
*
* @property object
* @property function Actions.doSomething
* @property function Actions.doSomethingElse
*
* @static
*/
PersistentEvent.Actions =
doSomething: function (args, cb)
// defaults
args = args || [];
// TODO check specific args here ...
var result = true,
err = null;
// do your action here, possibly with passed args
cb(err, result);
,
doSomethingElse: function (args, cb)
// defaults
args = args || [];
// TODO check specific args here ...
var result = true,
err = null;
// do your action here, possibly with passed args
cb(err, result);
;
/**
* Cache of all PersistentEvents
*
* @type Array.<PersistentEvent>
* @static
*/
PersistentEvent.Cache = [];
// Data Management
/**
* Connection to persistent storage.
* TODO - This should be abstracted to handle other engines that MySQL.
* @property object
* @static
*/
PersistentEvent.StorageConnection = null;
/**
* Sets the storage connection used to persist events.
*
* @param object storageConnection
* @static
*/
PersistentEvent.setStore = function (storageConnection) // set the persistent storage connection
// TODO - check args here...
// Note: this function isn't really needed unless you're using other kinds of storage engines
// where you'd want to test what engine was used and mutate this interface accordingly.
PersistentEvent.StorageConnection = storageConnection;
;
/**
* Saves a PersistentEvent to StorageConnection.
*
* @param PersistentEvent event - event to save
* @param function cb - callback on complete
* @static
*/
PersistentEvent.save$ = function (event, cb)
var conn = PersistentEvent.StorageConnection;
if (null === conn)
throw new Error('requires a StorageConnection');
// TODO - check for active connection here...
// TODO - check args here...
conn.query('INSERT INTO TABLE when = :when, what = :what, args = :args, pending = :pending', event, cb);
;
/**
* Loads all PersistentEvents from StorageConnection.
* @param function cb -- callback on complete
* @static
*/
PersistentEvent.loadAll$ = function (cb)
var conn = PersistentEvent.StorageConnection;
if (null === conn)
throw new Error('requires a StorageConnection');
// check for active connection here...
// check args here...
conn.query('QUERY * FROM TABLE WHERE pending = true', function (err, results)
if (err)
return cb(err);
results.forEach(function (result)
// TODO: check for existence of required fields here...
var event = new PersistentEvent(result.when, result.what, result.args, true);
event.schedule();
);
cb(null);
);
;
// ==== PersistentEvent Methods ====
/**
* Initialize an instance of PersistentEvent.
*
* @param object opts
* @return PersistentEvent
*/
Event.prototype.init = function (opts)
// check args
if ('object' !== typeof opts)
throw new Error('opts must be an object');
// set defaults
opts.args = opts.args || [];
opts.pending = opts.pending || true;
// convert string to Date, if required
if ('string' === typeof opts.when)
opts.when = new Date(opts.when);
// check that opts contains needed properties
if (!opts.when instanceof Date)
throw new Error('when must be a string representation of a Date or a Date object');
if ('string' !== typeof opts.what)
throw new Error('what must be a string containing an action name');
if (!Array.isArray(opts.args))
throw new Error('args must be an array');
if ('boolean' !== typeof opts.pending)
throw new Error('pending must be a boolean');
// set our properties
var self = this;
Object.keys(opts).forEach(function (key)
if (opts.hasOwnProperty(key))
self = opts[key];
);
return this;
;
/**
* Override for Object.toString()
* @returns string
*/
PersistentEvent.prototype.toString = function ()
return JSON.stringify(this);
;
/**
* Schedule the event to run.<br/>
* <em>Side-effect: saves event to persistent storage.</em>
*/
PersistentEvent.prototype.schedule = function ()
var self = this,
handler = Actions[this.what];
if ('function' !== typeof handler)
throw new Error('no handler found for action:' + this.what);
PersistentEvent.save$(self, function ()
self._event = scheduler.scheduleJob(self.when, function ()
handler(self.args, function (err, result)
if (err)
console.error('event ' + self + ' failed:' + err);
self.setComplete();
);
);
);
;
/**
* Sets this event complete.<br/>
* <em>Side-effect: saves event to persistent storage.</em>
*/
PersistentEvent.prototype.setComplete = function ()
var self = this;
delete this._event;
this.pending = false;
PersistentEvent.save$(this, function (err)
if (err)
console.error('failed to save event ' + self + ' :' + err);
);
;
请注意,这是一个首次通过样板,向您展示了一种设计问题解决方案的方法。它将需要您进一步努力才能运行。
【讨论】:
那你应该看看Mysql,它是Mysql数据库引擎的nodejs连接器。 对不起 Rob,但我的问题不在于存储。我在问我应该如何以及在哪里实施这些节点计划。我不想在我的 sql 表上运行 for 循环来检查是否达到约会日期。我应该将它作为全局变量存储在我的 app.js 中的数组中吗? @HiradRoshandel:您可能希望重新阅读node-schedule
的文档,因为它会在预定的日期时间处理“触发”您的事件。您不需要自己这样做,只需为您希望发生的每个事件创建一个节点调度实例。虽然您可以通过 cron
执行此操作,但这样做需要单独的脚本。
这太棒了@rob-raisch,正是我想要让我开始的东西【参考方案2】:
您可以每天早上有一个 cron 作业,它将挑选当天的所有约会并为他们安排推送。这样你就必须在服务器负载最小的时候查询一次数据库。
【讨论】:
我会测试它,看看它是否能解决我的问题,但现在 +1 谢谢以上是关于在哪里存储我的节点计划的主要内容,如果未能解决你的问题,请参考以下文章
使用 AWS Elastic Beanstalk 我应该在哪里存储我的应用程序代码?
助力屏幕经济发展,Thomas开启全球节点计划,托马斯中文社区正式成立
助力屏幕经济发展,Thomas开启全球节点计划,托马斯中文社区正式成立