beanstalk 生命周期

Table of content:

About

Beanstalk 是一个高性能,轻量级的分布式内存队列系统,最初设计的目的是想通过后台异步执行耗时的任务来降低高容量 Web 应用系统的页面访问延迟。

  1. 协议,类 Memcached 协议, 非二进制安全
  2. 全内存, 可开启 binlog, 断电从 binlog 恢复数据
  3. 单线程, 使用 epoll/kqueue 来实现事件机制

核心概念和生命周期:

核心概念:

  • job,一个需要异步处理的基本单元,job 要放在 tube 中。有多个生命周期,
  • tube,一个任务队列, 可以通过 use tube_name 来进行切换
  • producer,生产者,通过 put 将 job 放入 tube
1
2
put <pri> <delay> <ttr> <bytes>\r\n
<data>\r\n
  1. delay = 0,进入就绪(READY)队列, 可以直接被消费。
  2. dealy > 0, 进入延时队列(DELAYED), 等到延时时间到了之后自动迁移就绪队列。
  • consumer,消费者,通过 reserve,bury,release,delete 来改变 job 状态

生命周期

beanstalk 一个 job 的生命周期: ready, reserved,delayed,buried

  • ready, 等待被消费
  • reserved, 如果 job 被取出,worker 将预定这个 job
  • delayed, 延迟被消费,等待特定时间之后,状态再迁移为 ready状态
  • buried, 等待唤醒,通常是 job 执行失败时

通常的状态:一个 job 进入到队列,是 ready 状态。reserved 是等待被消费,然后被消费后是删除

1
2
 put            reserve               delete
-----> [READY] ---------> [RESERVED] --------> *poof*

还有一种可能的状态:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
put with delay               release with delay
----------------> [DELAYED] <------------.
| |
kick | (time passes) |
| |
put v reserve | delete
-----------------> [READY] ---------> [RESERVED] --------> *poof*
^ ^ | |
| \ release | |
| `-------------' |
| |
| kick |
| |
| bury |
[BURIED] <---------------'
|
| delete
`--------> *poof*

这里面有几个有趣的 job stats:

  • ttr(time-to-run) 任务超时重发,Beanstalkd 把任务返回给消费者后:消费者必须在预设的 TTR(time-to-run) 时间内发送 delete、release 或者 bury 命令改变任务状态;否则 Beanstalkd 会认为消息处理失败,任务将会被 release 到 ready 队列,其他消费者节点会执行执行。如果消费者预计在TTR时间内无法完成任务, 可以发送 touch 命令,以使 Beanstalkd 重新计算 TTR。Beanstalk 默认的 ttr 是 120s,也就是如果任务执行超过了 ttr,就有被重复执行的风险。
  • priority,任务 (job) 可以有 0~2^32 个优先级,0 表示优先级最高。Beanstalkd 采用最大最小堆 (Min-max heap) 处理任务优先级排序, 任何时刻调用 reserve 命令的消费者总是能拿到当前优先级最高的任务, 时间复杂度为 O(logn)
  • reserves, 被取出的次数
  • kicks 被 kick 的次数
  • timeouts, 任务 timeout 的次数(而这个在中文协议上翻译的是超时时间,是有严重问题的,在这个 MR 中提交了修复 https://github.com/beanstalkd/beanstalkd/pull/388)
  • id, job id

Beanstalk service

1
2
3
# install
sudo apt-get install beanstalkd # Linux
brew install beanstalkd # Mac

run service

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
ᐅ beanstalkd help
beanstalkd: unknown argument: man
Use: beanstalkd [OPTIONS]

Options:
-b DIR wal directory
-f MS fsync at most once every MS milliseconds (use -f0 for "always fsync")
-F never fsync (default)
-l ADDR listen on address (default is 0.0.0.0)
-p PORT listen on port (default is 11300)
-u USER become user and group
-z BYTES set the maximum job size in bytes (default is 65535)
-s BYTES set the size of each wal file (default is 10485760)
(will be rounded up to a multiple of 512 bytes)
-c compact the binlog (default)
-n do not compact the binlog
-v show version information
-V increase verbosity
-h show this help
1
beanstalkd -l 127.0.0.1 -p 11300  

常见的问题

集群问题

跟 memcache 类似,没有 master-slave 方式,需要自己解决单点问题

timeout limit 的含义

中文翻译有一些严重问题,导致设计基于 Beanstalk 的离线任务框架的时候,timeout limit 默认值设计的并不是很合理。后来提交了 MR 进行修复 https://github.com/beanstalkd/beanstalkd/pull/388

怎么可以限制一个 Task 被执行的数量 ?怎样避免任务被反复执行 ?

一个任务可能被反复执行的场景有:

  • 任务时间过长,超过了默认的 timeout 时间(120s)或者 task 设置的 ttr, 会被重新 release 到 ready 队列,等待被其他消费节点执行
  • 任务执行过程中抛了异常,会进入 bury 状态,在一定时间内会重新 kick 到 ready 状态,等待被消费。

能够更准备代表一个任务被消费次数的是 reserves ,详见: https://github.com/kr/beanstalkd/blob/master/doc/protocol.txt#L81

todos

还可以从两个角度来扩展这个话题:

  • beanstalk 一些源码阅读
  • 工作中基于 beanstalk 的离线任务框架的简化版设计图

Reference and Recommendation

关于头图

拍摄自青岛极地海洋世界

youtube-dl download 的设计
Redis RDB 持久化遇到的问题