bull README

The project's README from github.com/OptimalBits/bull. As one markdown file: README.md.




The fastest, most reliable, Redis-based queue for Node.
Carefully written for rock solid stability and atomicity.


Sponsors · Features · UIs · Install · Quick Guide · Documentation

Check the new Guide!


🚀 Sponsors 🚀

Dragonfly Dragonfly is a new Redis™ drop-in replacement that is fully compatible with BullMQ and brings some important advantages over Redis™ such as massive better performance by utilizing all CPU cores available and faster and more memory efficient data structures. Read more here on how to use it with BullMQ.

📻 News and updates

Bull is currently in maintenance mode, we are only fixing bugs. For new features check BullMQ, a modern rewritten implementation in Typescript. You are still very welcome to use Bull if it suits your needs, which is a safe, battle tested library.

Follow me on Twitter for other important news and updates.

🛠 Tutorials

You can find tutorials and news in this blog: https://blog.taskforce.sh/


Used by

Bull is popular among large and small organizations, like the following ones:

Atlassian Autodesk Mozilla Nest Salesforce


Official FrontEnd

Taskforce.sh, Inc

Supercharge your queues with a professional front end:

Sign up at Taskforce.sh


Bull Features

And coming up on the roadmap...


UIs

There are a few third-party UIs that you can use for monitoring:

BullMQ

Bull v3

Bull <= v2


Monitoring & Alerting


Feature Comparison

Since there are a few job queue solutions, here is a table comparing them:

Feature BullMQ-Pro BullMQ Bull Kue Bee Agenda
Backend redis redis redis redis redis mongo
Observables ✓
Group Rate Limit ✓
Group Support ✓
Batches Support ✓
Parent/Child Dependencies ✓ ✓
Priorities ✓ ✓ ✓ ✓ ✓
Concurrency ✓ ✓ ✓ ✓ ✓ ✓
Delayed jobs ✓ ✓ ✓ ✓ ✓
Global events ✓ ✓ ✓ ✓
Rate Limiter ✓ ✓ ✓
Pause/Resume ✓ ✓ ✓ ✓
Sandboxed worker ✓ ✓ ✓
Repeatable jobs ✓ ✓ ✓ ✓
Atomic ops ✓ ✓ ✓ ✓
Persistence ✓ ✓ ✓ ✓ ✓ ✓
UI ✓ ✓ ✓ ✓ ✓
Optimized for Jobs / Messages Jobs / Messages Jobs / Messages Jobs Messages Jobs

Install

npm install bull --save

or

yarn add bull

Requirements: Bull requires a Redis version greater than or equal to 2.8.18.

Typescript Definitions

npm install @types/bull --save-dev
yarn add --dev @types/bull

Definitions are currently maintained in the DefinitelyTyped repo.

Contributing

We welcome all types of contributions, either code fixes, new features or doc improvements. Code formatting is enforced by prettier. For commits please follow conventional commits convention. All code must pass lint rules and test suites before it can be merged into develop.


Quick Guide

Basic Usage

const Queue = require('bull');

const videoQueue = new Queue('video transcoding', 'redis://127.0.0.1:6379');
const audioQueue = new Queue('audio transcoding', { redis: { port: 6379, host: '127.0.0.1', password: 'foobared' } }); // Specify Redis connection using object
const imageQueue = new Queue('image transcoding');
const pdfQueue = new Queue('pdf transcoding');

videoQueue.process(function (job, done) {

  // job.data contains the custom data passed when the job was created
  // job.id contains id of this job.

  // transcode video asynchronously and report progress
  job.progress(42);

  // call done when finished
  done();

  // or give an error if error
  done(new Error('error transcoding'));

  // or pass it a result
  done(null, { framerate: 29.5 /* etc... */ });

  // If the job throws an unhandled exception it is also handled correctly
  throw new Error('some unexpected error');
});

audioQueue.process(function (job, done) {
  // transcode audio asynchronously and report progress
  job.progress(42);

  // call done when finished
  done();

  // or give an error if error
  done(new Error('error transcoding'));

  // or pass it a result
  done(null, { samplerate: 48000 /* etc... */ });

  // If the job throws an unhandled exception it is also handled correctly
  throw new Error('some unexpected error');
});

imageQueue.process(function (job, done) {
  // transcode image asynchronously and report progress
  job.progress(42);

  // call done when finished
  done();

  // or give an error if error
  done(new Error('error transcoding'));

  // or pass it a result
  done(null, { width: 1280, height: 720 /* etc... */ });

  // If the job throws an unhandled exception it is also handled correctly
  throw new Error('some unexpected error');
});

pdfQueue.process(function (job) {
  // Processors can also return promises instead of using the done callback
  return pdfAsyncProcessor();
});

videoQueue.add({ video: 'http://example.com/video1.mov' });
audioQueue.add({ audio: 'http://example.com/audio1.mp3' });
imageQueue.add({ image: 'http://example.com/image1.tiff' });

Using promises

Alternatively, you can return promises instead of using the done callback:

videoQueue.process(function (job) { // don't forget to remove the done callback!
  // Simply return a promise
  return fetchVideo(job.data.url).then(transcodeVideo);

  // Handles promise rejection
  return Promise.reject(new Error('error transcoding'));

  // Passes the value the promise is resolved with to the "completed" event
  return Promise.resolve({ framerate: 29.5 /* etc... */ });

  // If the job throws an unhandled exception it is also handled correctly
  throw new Error('some unexpected error');
  // same as
  return Promise.reject(new Error('some unexpected error'));
});

Separate processes

The process function can also be run in a separate process. This has several advantages:

In order to use this feature just create a separate file with the processor:

// processor.js
module.exports = function (job) {
  // Do some heavy work

  return Promise.resolve(result);
}

And define the processor like this:

// Single process:
queue.process('/path/to/my/processor.js');

// You can use concurrency as well:
queue.process(5, '/path/to/my/processor.js');

// and named processors:
queue.process('my processor', 5, '/path/to/my/processor.js');

Repeated jobs

A job can be added to a queue and processed repeatedly according to a cron specification:

  paymentsQueue.process(function (job) {
    // Check payments
  });

  // Repeat payment job once every day at 3:15 (am)
  paymentsQueue.add(paymentsData, { repeat: { cron: '15 3 * * *' } });

As a tip, check your expressions here to verify they are correct: cron expression generator

Pause / Resume

A queue can be paused and resumed globally (pass true to pause processing for just this worker):

queue.pause().then(function () {
  // queue is paused now
});

queue.resume().then(function () {
  // queue is resumed now
})

Events

A queue emits some useful events, for example...

.on('completed', function (job, result) {
  // Job completed with output result!
})

For more information on events, including the full list of events that are fired, check out the Events reference

Queues performance

Queues are cheap, so if you need many of them just create new ones with different names:

const userJohn = new Queue('john');
const userLisa = new Queue('lisa');
.
.
.

However every queue instance will require new redis connections, check how to reuse connections or you can also use named processors to achieve a similar result.

Cluster support

NOTE: From version 3.2.0 and above it is recommended to use threaded processors instead.

Queues are robust and can be run in parallel in several threads or processes without any risk of hazards or queue corruption. Check this simple example using cluster to parallelize jobs across processes:

const Queue = require('bull');
const cluster = require('cluster');

const numWorkers = 8;
const queue = new Queue('test concurrent queue');

if (cluster.isMaster) {
  for (let i = 0; i < numWorkers; i++) {
    cluster.fork();
  }

  cluster.on('online', function (worker) {
    // Let's create a few jobs for the queue workers
    for (let i = 0; i < 500; i++) {
      queue.add({ foo: 'bar' });
    };
  });

  cluster.on('exit', function (worker, code, signal) {
    console.log('worker ' + worker.process.pid + ' died');
  });
} else {
  queue.process(function (job, jobDone) {
    console.log('Job done by worker', cluster.worker.id, job.id);
    jobDone();
  });
}

Documentation

For the full documentation, check out the reference and common patterns:

If you see anything that could use more docs, please submit a pull request!


Important Notes

The queue aims for an "at least once" working strategy. This means that in some situations, a job could be processed more than once. This mostly happens when a worker fails to keep a lock for a given job during the total duration of the processing.

When a worker is processing a job it will keep the job "locked" so other workers can't process it.

It's important to understand how locking works to prevent your jobs from losing their lock - becoming stalled - and being restarted as a result. Locking is implemented internally by creating a lock for lockDuration on interval lockRenewTime (which is usually half lockDuration). If lockDuration elapses before the lock can be renewed, the job will be considered stalled and is automatically restarted; it will be double processed. This can happen when:

  1. The Node process running your job processor unexpectedly terminates.
  2. Your job processor was too CPU-intensive and stalled the Node event loop, and as a result, Bull couldn't renew the job lock (see #488 for how we might better detect this). You can fix this by breaking your job processor into smaller parts so that no single part can block the Node event loop. Alternatively, you can pass a larger value for the lockDuration setting (with the tradeoff being that it will take longer to recognize a real stalled job).

As such, you should always listen for the stalled event and log this to your error monitoring system, as this means your jobs are likely getting double-processed.

As a safeguard so problematic jobs won't get restarted indefinitely (e.g. if the job processor always crashes its Node process), jobs will be recovered from a stalled state a maximum of maxStalledCount times (default: 1).

bull (OptimalBits/bull)

Searching Database…

16.3K ·GitHub
这是什么东西?Bull 是一个基于 Redis 的 Node.js 队列包,用于处理分布式任务和消息——后台任务、定时任务和消息传递,专为极其可靠的稳定性和原子性而设计。
它是需要引入到我的 Node 应用里的库,还是需要单独部署的东西?它是一个 npm 库,直接在你的 Node.js 应用里 require 即可——不需要单独部署任何 Bull 服务器。队列状态保存在你的 Redis 实例中,任务由你代码里的 worker 来处理。
怎么把它加到我的项目里?先用 npm install bull 安装这个包,然后创建一个命名队列,并绑定一个处理函数来处理每个任务。README 里附有入门指南、完整的 API 参考和常见用法示例。
我需要同时运行 Redis 吗?需要——Bull 把所有队列状态都存在 Redis 里,Redis 版本必须是 2.8.18 或更新,另外还需要 Node.js 12 或更高版本。TypeScript 类型定义则在 DefinitelyTyped 仓库中单独维护。
一个任务有没有可能被处理不止一次?有可能——Bull 是“至少一次”语义:如果某个 worker 丢失了锁(比如崩溃,或者某个吃 CPU 的任务阻塞了事件循环),该任务会被重新执行,可能跑两次。因卡住而重启的次数上限是 maxStalledCount(默认 1);请留意 stalled 事件。
宣传语说 Bull 是“Premium Queue”——用它要花钱吗?不用——Bull 是基于 MIT 许可证的开源软件,可以免费使用。Taskforce.sh 是一个可选的托管队列前端,属于另外的独立服务,需要单独注册使用。
它开箱即用能做哪些事?延迟任务、按 cron 计划重复执行的任务、重试、优先级、并发控制、任务限流、全局或按 worker 暂停/恢复、一个队列支持多种任务类型、沙箱化的处理器,以及进程崩溃后的自动恢复。
有没有可以查看队列的界面?有——README 里列了好几个第三方工具:bull-board、bull-repl、bull-monitor、Matador、Toureiro 等等,另外还有 Taskforce.sh 提供的托管前端,带有队列总览、任务检查和指标。此外还有一个 Prometheus exporter 可用于告警。
它和 Kue、Bee、Agenda 相比怎么样?它们都是任务队列,但 Kue 和 Bee 基于 Redis,而 Agenda 把任务存在 MongoDB 里。根据 README 的对比表,三者中只有 Bull 具有限流、暂停/恢复和沙箱 worker;Bee 不支持优先级,Agenda 则没有全局事件和暂停/恢复功能。
任务处理函数可以不在我的主 Node 进程里运行吗?可以——处理器可以放在单独的文件里以沙箱方式运行:那里崩溃不会拖垮你的 worker,阻塞的代码也不会卡住队列,而且能用更少的 Redis 连接更好地利用多核 CPU。从 3.2.0 版本开始,官方推荐使用基于线程的处理器。
What are similar projects?rq, effect-mq, xxl-job, celery, sidekiq, redisson, asynq, tork
Size
4.1 MB
Stars
16.3K
Age
13y
Commits · 90 Days