Node.js task scheduler for multi-server infrastructure

栏目: IT技术 · 发布时间: 4年前

内容简介:Simple package with similar API to nativeMulti-instance task manager for Node.js. This package has the support of clusters, multi-server and multi-threaded Node.js instances. This package goal is to make sure that the only single process of eachCan be easi

JoSk

Node.js task scheduler for multi-server infrastructure Node.js task scheduler for multi-server infrastructure

Simple package with similar API to native setTimeout and setInterval methods, but synced between all running Node.js instances via MongoDB Collection.

Multi-instance task manager for Node.js. This package has the support of clusters, multi-server and multi-threaded Node.js instances. This package goal is to make sure that the only single process of each task ( job / cron ) is running across multi-server ( multi-thread / multi-instance ) setup.

This is a server-only package.

Main features:

  • :man:‍:microscope: ~90% tests coverage;
  • :package: Zero dependencies, written from scratch for top performance;
  • :office: Synchronize single task across multiple servers;
  • :muscle: Bulletproof design, built-in retries, and "zombie" task recovery :gun: .

Install:

# for node@>=8.9.0
npm install josk --save

# for node@<8.9.0
npm install josk@=1.1.0 --save
const JoSk = require('josk');

//ES6 Style:
import JoSk from 'josk';

Install Meteor:

meteor add ostrio:cron-jobs
import JoSk from 'meteor/ostrio:cron-jobs';

Known Meteor Issues:

Error: Can't wait without a fiber

Can be easily solved via "bounding to Fiber":

const bound = Meteor.bindEnvironment((callback) => {
  callback();
});

const db  = Collection.rawDatabase();
const job = new JoSk({db: db});

const task = (ready) => {
  bound(() => { // <-- use "bound" inside of a task
    ready();
  });
};

job.setInterval(task, 60 * 60 * 1000, 'task');

Notes:

  • This package is perfect when you have multiple servers for load-balancing, durability, an array of micro-services or any other solution with multiple running copies of code when you need to run repeating tasks, and you need to run it only once per app, not per server.
  • Limitation — task must be run not often than once per two seconds (from 2 to ∞ seconds). Example tasks: Email, SMS queue, Long-polling requests, Periodical application logic operations or Periodical data fetch and etc.
  • Accuracy — Delay of each task depends on MongoDB and "de-synchronization delay". Trusted time-range of execution period is task_delay ± (256 + MongoDB_Connection_And_Request_Delay) . That means this package won't fit when you need to run a task with very certain delays. For other cases, if ±256 ms delays are acceptable - this package is the great solution.
  • Use opts.minRevolvingDelay and opts.maxRevolvingDelay to set the range for random delays between executions. Revolving range acts as a safety control to make sure different servers not picking the same task at the same time. Default values ( 32 and 256 ) are the best for 3-server setup ( the most common topology ). Tune these options to match needs of your project. Higher opts.minRevolvingDelay will reduce load on MongoDB.
  • To avoid "DB locks" — it's recommended to use separate DB from "main" application DB ( same MongoDB server can have multiple DBs )

API:

new JoSk({opts}) :

  • opts.db { Object } - [Required] Connection to MongoDB, like returned as argument from MongoClient.connect()
  • opts.prefix { String } - [Optional] use to create multiple named instances
  • opts.autoClear { Boolean } - [Optional] Remove ( Clear ) obsolete tasks ( any tasks which are not found in the instance memory (runtime), but exists in the database ). Obsolete tasks may appear in cases when it wasn't cleared from the database on process shutdown, and/or was removed/renamed in the app. Obsolete tasks may appear if multiple app instances running different codebase within the same database, and the task may not exist on one of the instances. Default: false
  • opts.resetOnInit { Boolean } - [Optional] make sure all old tasks is completed before set new one. Useful when you run only one instance of app, or multiple app instances on one machine, in case machine was reloaded during running task and task is unfinished
  • opts.zombieTime { Number } - [Optional] time in milliseconds, after this time - task will be interpreted as " zombie ". This parameter allows to rescue task from " zombie mode" in case when: ready() wasn't called, exception during runtime was thrown, or caused by bad logic. While resetOnInit option helps to make sure tasks are done on startup, zombieTime option helps to solve same issue, but during runtime. Default value is 900000 ( 15 minutes ). It's not recommended to set this value to less than a minute ( 60000ms )
  • opts.minRevolvingDelay { Number } - [Optional] Minimum revolving delay — the minimum delay between tasks executions in milliseconds. Default: 32
  • opts.maxRevolvingDelay { Number } - [Optional] Maximum revolving delay — the maximum delay between tasks executions in milliseconds. Default: 256
  • opts.onError { Function } - [Optional] Informational hook, called instead of throwing exceptions. Default: false . Called with two arguments:
    • title { String }
    • details { Object }
    • details.description { String }
    • details.error { Mix }
    • details.uid { String } - Internal uid , suitable for .clearInterval() and .clearTimeout()
  • opts.onExecuted { Function } - [Optional] Informational hook, called when task is finished. Default: false . Called with two arguments:
    • uid { String } - uid passed into .setImmediate() , .setTimeout() , or setInterval() methods
    • details { Object }
    • details.uid { String } - Internal uid , suitable for .clearInterval() and .clearTimeout()
    • details.date { Date } - Execution timestamp as JS { Date }
    • details.delay { Number } - Execution delay (e.g. interval for .setInterval() )
    • details.timestamp { Number } - Execution timestamp as unix { Number }

Initialization:

MongoClient.connect('url', (error, client) => {
  // To avoid "DB locks" — it's a good idea to use separate DB from "main" application DB
  const db = client.db('dbName');
  const job = new JoSk({db: db});
});

Initialization in Meteor:

// Meteor.users.rawDatabase() is available in most Meteor setups
// If this is not your case, you can use `rawDatabase` form any other collection
const db  = Meteor.users.rawDatabase();
const job = new JoSk({db: db});

Note: This library relies on job ID, so you can not pass same job (with the same ID). Always use different uid , even for the same task:

const task = function (ready) {
  //...some code here
  ready();
};

job.setInterval(task, 60 * 60 * 1000, 'task-1000');
job.setInterval(task, 60 * 60 * 2000, 'task-2000');

Passing arguments ( not really fancy solution, sorry ):

const job = new JoSk({db: db});
let globalVar = 'Some top level or env.variable (can be changed during runtime)';

const task = function (arg1, arg2, ready) {
  //...some code here
  ready();
};

const taskB = function (ready) {
  task(globalVar, 'b', ready);
};

const task1 = function (ready) {
  task(1, globalVar, ready);
};

job.setInterval(taskB, 60 * 60 * 1000, 'taskB');
job.setInterval(task1, 60 * 60 * 1000, 'task1');

Note: To clean up old tasks via MongoDB use next query pattern:

// Run directly in MongoDB console:
db.getCollection('__JobTasks__').remove({});
// If you're using multiple JoSk instances with prefix:
db.getCollection('__JobTasks__PrefixHere').remove({});

setInterval(func, delay, uid)

  • func { Function } - Function to call on schedule
  • delay { Number } - Delay for first run and interval between further executions in milliseconds
  • uid { String } - Unique app-wide task id

Set task into interval execution loop. ready() is passed as the first argument into task function.

In this example, next task will not be scheduled until the current is ready:

const syncTask = function (ready) {
  //...run sync code
  ready();
};
const asyncTask = function (ready) {
  asyncCall(function () {
    //...run more async code
    ready();
  });
};

job.setInterval(syncTask, 60 * 60 * 1000, 'syncTask');
job.setInterval(asyncTask, 60 * 60 * 1000, 'asyncTask');

In this example, next task will not wait for the current task to finish:

const syncTask = function (ready) {
  ready();
  //...run sync code
};
const asyncTask = function (ready) {
  ready();
  asyncCall(function () {
    //...run more async code
  });
};

job.setInterval(syncTask, 60 * 60 * 1000, 'syncTask');
job.setInterval(asyncTask, 60 * 60 * 1000, 'asyncTask');

In this example, we're assuming to have long running task, executed in a loop without delay, but after full execution:

const longRunningAsyncTask = function (ready) {
  asyncCall((error, result) => {
    if (error) {
      ready(); // <-- Always run `ready()`, even if call was unsuccessful
    } else {
      anotherCall(result.data, ['param'], (error, response) => {
        waitForSomethingElse(response, () => {
          ready(); // <-- End of full execution
        });
      });
    }
  });
};

job.setInterval(longRunningAsyncTask, 0, 'longRunningAsyncTask');

setTimeout(func, delay, uid)

  • func { Function } - Function to call on schedule
  • delay { Number } - Delay in milliseconds
  • uid { String } - Unique app-wide task id

Set task into timeout execution. setTimeout is useful for cluster - when you need to make sure task was executed only once. ready() is passed as the first argument into task function.

const syncTask = function (ready) {
  //...run sync code
  ready();
};
const asyncTask = function (ready) {
  asyncCall(function () {
    //...run more async code
    ready();
  });
};

job.setTimeout(syncTask, 60 * 60 * 1000, 'syncTask');
job.setTimeout(asyncTask, 60 * 60 * 1000, 'asyncTask');

setImmediate(func, uid)

  • func { Function } - Function to execute
  • uid { String } - Unique app-wide task id

Immediate execute the function, and only once. setImmediate is useful for cluster - when you need to execute function immediately and only once across all servers. ready() is passed as the first argument into the task function.

const syncTask = function (ready) {
  //...run sync code
  ready();
};
const asyncTask = function (ready) {
  asyncCall(function () {
    //...run more async code
    ready();
  });
};

job.setImmediate(syncTask, 'syncTask');
job.setImmediate(asyncTask, 'asyncTask');

clearInterval(timer)

Cancel (abort) current interval timer. Must be called in a separate event loop from setInterval .

const timer = job.setInterval(func, 34789, 'unique-taskid');
job.clearInterval(timer);

clearTimeout(timer)

Cancel (abort) current timeout timer. Should be called in a separate event loop from setTimeout .

const timer = job.setTimeout(func, 34789, 'unique-taskid');
job.clearTimeout(timer);

Running Tests

  1. Clone this package
  2. In Terminal ( Console ) go to directory where package is cloned
  3. Then run:
# Before run tests make sure NODE_ENV === development
# Install NPM dependencies
npm install --save-dev

# Before run tests you need to have running MongoDB
MONGO_URL="mongodb://127.0.0.1:27017/npm-josk-test-001" npm test

# Be patient, tests are taking around 2 mins

Running Tests in Meteor environment

# Default
meteor test-packages ./ --driver-package=meteortesting:mocha

# With custom port
meteor test-packages ./ --driver-package=meteortesting:mocha --port 8888

# With local MongoDB and custom port
MONGO_URL="mongodb://127.0.0.1:27017/meteor-josk-test-001" meteor test-packages ./ --driver-package=meteortesting:mocha --port 8888

# Be patient, tests are taking around 2 mins

Why JoSk?

JoSk is Job-Task - Is randomly generated name by "uniq" project

Support our open source contribution:


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

长尾理论

长尾理论

[美]克里斯•安德森 (Chris Anderson) / 乔江涛、石晓燕 / 中信出版社 / 2012 / 68.00元

网络经济正如火如荼地发展着,长尾理论无疑成为当代商务人士最为关注的焦点之一。不论是关于长尾理论的溢美还是论战,都代表了其备受关注的程度。 《长尾理论》是克里斯•安德森对这些争论的最明确的回答。在书中,他详细阐释了长尾的精华所在,指出商业和文化的未来不在于传统需求曲线上那个代表“畅销商品”的头部,而是那条代表“冷门商品”的经常被人遗忘的长尾。他还揭示了长尾现象是如何从工业资本主义原动力——规模......一起来看看 《长尾理论》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码