【问题标题】: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 循环来检查日期

【问题讨论】:

    标签: node.js express scheduled-tasks


    【解决方案1】:

    您需要通过使用SQLite 之类的方式写入本地文件、运行您自己的数据库服务器(例如MongoDB)或使用基于云的存储服务,将您的应用程序数据持久化到某种形式的永久存储中喜欢Amazon SimpleDb

    这些选项中的每一个(以及许多其他选项)都具有可用于读取/写入/删除持久数据的 npm 模块。例如,请参阅 MongoDbSQLite3SimpleDb,所有这些都可以在 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 谢谢
    猜你喜欢
    • 2014-01-27
    • 2018-12-21
    • 2013-12-25
    • 2012-03-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-17
    • 1970-01-01
    相关资源
    最近更新 更多