beanstalkd
协议
描述
beanstalkd 协议 运行在 TCP 层上,并采用了 ASCII 编码。
大致工作流程为:开启客户端连接、发送命令和数据、等待响应、关闭连接。
对于每一个连接而言,服务器会按照接收到指令的顺序一一执行这些指令,并且按照同样的顺序,发送响应。
在本协议中,所有涉及到的整数,都是指十进制,且为非负,除非另有说明。
命名约定
只支持 ASCII 字符进行命名
其支持的字符有:
- 字母( A-Z、a-z )
 - 数字( 0-9 )
 - 小短横( - )
 - 加号( + )
 - 正斜杠( / )
 - 英文封号( ; )
 - 英文点号( . )
 - 美元符( $ )
 - 英文下划线( _ )
 - 英文左右小括号( “(“ 和 “)” )
注意:命名不可以 - 作为开头,当遇到空格或换行时,会视作命名结束。每个命名的长度,至少为 1+ 个字符。
 
Errors
| 错误类型 | 描述 | 
|---|---|
| OUT_OF_MEMORY\r\n | 服务器内存不够分配了,可能需要过一会再尝试 | 
| INTERNAL_ERROR\r\n | 这说明服务器,也就是 beanstalkd 内部出现了 bug ,理论上不应该出现这个错误,如果出现了,请将这个 bug 提交到 google group | 
| BAD_FORMAT\r\n | 发送的命令格式错误。比如,命令行不是以 \r\n 结尾,又或者在该传入数值的地方传入了非数值,也可能参数数量错误等一切命令格式有误的可能性。 | 
| UNKMOWM_COMMAND\r\n | 使用了未定义的命令(找不到这个命令),此时可能要检查是否拼写有错 | 
Job 生命周期
一个 Job 由 put 命令创建,在它的生命周期以内,它必将处于以下四种状态中的一种:「ready」、「reserved」、「delayed」、「buried」
当使用完 put 命令,Job 一般从 「ready」 开始,它会在「ready」队列中等待,直到有「reserved」命令过来,当接收成功之后,则将该 Job 放入到 「reserved」 队列。接着,当进程处理完这个 Job 之后,则会发送一个「delete」命令,将这个 Job 从 beanstalkd 中删除。
| 状态 | 描述 | 
|---|---|
| ready | 被放入 Tube 之后等待被接收和处理 | 
| reserved | 当 Job 被 reserve 命令接收,Job 会进入这个状态,它代表被接收,但还没有得到其他反馈 | 
| delayed | 延迟状态,等时间到了会变成 ready 状态 | 
| buried | 预留状态,一般当消费者处理失败时,会将它设置为预留 | 
图示 Job 生命周期:
put reserve delete-----> [READY] ---------> [RESERVED] --------> *poof*
当然,它也可能经历更复杂的演化,如下图:
put with delay release with delay----------------> [DELAYED] <------------.| |kick | (time passes) || |put v reserve | delete-----------------> [READY] ---------> [RESERVED] --------> *poof*^ ^ | || \ release | || `-------------' || || kick || || bury |[BURIED] <---------------'|| delete`--------> *poof*
Tubes
一个 beanstalkd 服务允许拥有多个 Tube ,每一个 Tube 包含两个队列: ready queue 和 delay queue 。
每个 Job 都必然会存在于某个 Tube 之下。
可以通过 watch 指令关注某个 Tube ,也可以通过 ignore 命令取消关注。
当你使用 watch list 命令时,它会返回你所关注的 tubes 。
当消费者开始接收 Job 的时候,Job 一般来自 watch 了的 Tube。
当一个客户端连接进来,watch list 最初只有一个名为 default 的 tube 。如果当存入 Job 时没有使用 use 命令指定 tube ,这个 Job 就会被放入到 default tube 中。
Tubes 会在你使用到它的时候创建,如果 Tube 变空了(没有 ready Job ,没有 delayed Job , 没有 buried Job),且没有客户端连接指向它,它就会被删掉。
命令
生产者命令
put
此命令用于向队列中插入 job ,命令格式如下:
put <pri> <delay> <ttr> <bytes>\r\n<data>\r\n
它默认会将 job 插入到当前所 use 的 tube , 这点可以参考下面的 use命令
| 选项 | 描述 | 
|---|---|
| pri | 这是一个整型数值,代表着这个 job 的优先级,数值越小,排队排在越前面,优先级最高为 0 ,最后面为 4294967295 | 
| delay | 这也是一个整型数值,是一个秒数,指多少秒之后将这个 job 放入到 ready queue 中,在那之前,这个 job 都将处于 delayed 状态 | 
| ttr | 这也是一个整型数值,是一个描述,指一个 job 被允许处理的最大时间,这个时间从 job 被 reserve 起开始计算,超过时间还未被 delete 、 release 、 bury ,则服务器会自动释放这个 job,并重新插入到 ready queue 中。此数值最小支持 1 ,如果传的是 0 ,则服务器会默认将它变成 1 | 
| bytes | 这是一个数值,用于指明这个 job body 的大小,不包含「\r\n」这两个字符。这个值必须小于 beanstalkd 配置的 max-job-size , 单位是 bytes | 
| data | 这是 job body ,上一行的 bytes 就是由此行除却「\r\n」计算得出的。 | 
当成功发送 put 命令后,客户端要等待响应,响应结果可能是如下几个:
| 响应 | 描述 | 
|---|---|
| INSERTED \r\n | 插入成功,id 是一个 interger ,标识新插入的 job | 
| BURIED \r\n | 如果服务器因为增加优先级队列而内存不足时会返回这个结果,id 是一个 interger ,标识新插入的 job | 
| EXPECTED_CRLF\r\n | job body 必须以「\r\n」结尾,这两个字节不用计入上一行的 bytes 计算中 | 
| JOB_TOO_BIG\r\n | job body 超出了 max-job-size 的限制 | 
| DRAINING\r\n | 目标服务器不再接收新的请求,需要尝试其他服务器,或断开连接之后晚点再重新尝试 | 
use
此命令为 Producer 提供,当发送此命令之后,后续的 put 命令,就会把 job 全部放入到此 use 命令指定的 tube 中。如果没有通过 use 指定 tube , 则会默认将 job 放入到 default tube 中。
use <tube>\r\n
| 选项 | 描述 | 
|---|---|
| tube | 一个最大不超过 200 bytes 的名称,它指定一个 tube ,如果这个 tube 不存在,则会自动创建 | 
| 响应 | 描述 | 
|---|---|
| USING \r\n | 是接下来开始使用的 tube | 
消费者命令
从 queue 中消费 job 会使用以下命令:
- reserve
 - delete
 - release
 - bury
reserve
另外,你还能指定接收的超时时间,如下:reserve\r\n
这个命令将会返回一个新的、reserved 状态的 jobreserve-with-timeout <seconds>\r\n
如果没有可用的 job 能被接收,则 beanstalkd 一直等到出现一个可接收的 job 之后再返回。
一旦一个 job 被客户端接收,客户端要在 ttr 指定的时间限定内处理 job ,否则,超时的话,服务器会将 job 重新放回 ready queue 中。
可以在 stats-job 命令的 response 中找到 ttr 的值和已经使用掉的时间。
如果处于 ready 状态的 job 不止一个,beanstalkd 将会选择一个 priority 最小的 job,如果 priority 相等,则会选择一个最先 put 的 job 。题外话:这里我怀疑 beanstalkd 的协议有一处写错了,原文为 Within each priority, it will choose the one that was reserved first. 我认为应该将 reserved 改为 put 。
 
如果指定的 timeout seconds 是 0 ,这将导致服务器立即返回 TIME_OUT 的响应(也有可能立即返回一个 job ,这取决于服务器的响应速度以及是否存在可接收的 job )
为 timeout 设置一个合理的 seconds ,可以限制客户端阻塞等待接收 job 的时间。
| 失败响应 | 描述 | 
|---|---|
| DEADLINE_SOON\r\n | ttr 的最后一秒,被服务器设定为安全界限,在此期间,该客户端不会接收到另外一个 job 。比如:客户端在安全界限时间里发送了一条 reserve 命令,或者,当一条 reserve 命令在等待反馈时,安全界限时间正好到期,这时候,都将得到一个 DEADLINE_SOON\r\n 的响应 | 
| TIMED_OUT\r\n | 当使用 reserve-with-timeout 命令,超过时间还未接收到 job ,又或者客户端连接已经关闭,此时会返回此值 | 
成功响应:
RESERVED <id> <bytes>\r\n<data>\r\n
| 参数 | 描述 | 
|---|---|
| id | job 的 id ,一个整型值,在这个 beanstalkd 服务器中具备全局唯一性 | 
| bytes | 表示 job data 的大小,不包含结束符 \r\n | 
| data | job data , 之前 put 时放入的 job data ,原模原样返回 | 
delete
delete 命令用于从服务器完全删除一个 job , 这一般用于客户端成功处理 job 之后。
客户端可以删除 reserved 的 job , 使 job 进入准备状态 , 延迟 job ,预留 job 。
delete <id>\r\n
| 选项 | 描述 | 
|---|---|
| id | job 的 id | 
| 响应 | 描述 | 
|---|---|
| DELETED\r\n | 删除成功 | 
| NOT_FOUND\r\n | 找不到这个 job ,或者这个 job 并非这台 client 接收的、或是 job 处于 ready 、 buried 状态。这很可能发生在 ttr 时间到了之后才发送 delete 命令的情况下。 | 
release
此命令可以把 reserved job 放回 ready 队列中,同时 job 的状态也会回到 ready ,release 之后,这个 job 可以被任何其他的客户端接收。
一般这个命令用在消费者处理 job 失败的情况下。
release <id> <pri> <delay>\r\n
| 选项 | 描述 | 
|---|---|
| id | job id | 
| pri | interger ,指定一个新的优先级,数值越小,越早被接收 | 
| delay | interger ,指定一个新的延迟,如果设置了预留值,则 job 的状态会是 delayed ,直到延迟时间到期 | 
| 响应 | 描述 | 
|---|---|
| RELEASED\r\n | 处理成功 | 
| BURIED\r\n | 因为新增优先级队列数据结构而导致内存溢出 | 
| NOT_FOUND\r\n | 没有找到这个 job 或 此 job 不是当前客户端接收的 | 
bury
这个命令可以将一个 job 操作为 buried 状态。
buried job 被存放在一个 FIFO (first input first out ,先进先出)的链表中,它不会被服务器再次操作,除非有客户端对它发起了 kick 命令。
bury <id> <pri>\r\n
| 选项 | 描述 | 
|---|---|
| id | job id | 
| pri | 优先级,一个整型数字,越小的越先被接收 | 
| 响应 | 描述 | 
|---|---|
| BURIED\r\n | 操作成功 | 
| NOT_FOUND\r\n | 找不到 job 或该 job 不是被当前客户端所接收 | 
touch
此命令能让当前消费者得到更多的执行 job 的时间。
比如,ttr 是用于避免消费者崩溃而导致 job 丢失,但同样也会误伤一批执行时间过长的消费者,实际上消费者没有崩溃,但执行时间已经超出了 ttr ,此时,通过 touch 命令,可以让客户端得到更多的处理时间,不先触发 ttr 机制。
当然,使用了 touch 命令,只是延长了 ttr 的时间,ttr 的机制仍然存在。
通过这个命令,消费者可以定期告诉服务器,当前处理程序仍处于活跃状态。
此命令不受 DEADLINE_SOON影响
touch <id>\r\n
| 选项 | 描述 | 
|---|---|
| id | job id | 
| 响应 | 描述 | 
|---|---|
| TOUCHED\r\n | 操作成功 | 
| NOT_FOUND\r\n | 没有找到这个 job 或者 该 job 不是这个客户端接收的 | 
watch
watch 命令会往 watch list 中添加一个 tube ,消费者通过 reserve ,可以接收到 watch list 中任何一个 tube 传来的 job 。一个新的连接,watch list 中默认存在一个 default tube 。
watch <tube>\r\n
| 选项 | 描述 | 
|---|---|
| tube | 200 bytes 以内的字符串,代表着 tube 的名字,如果该 tube 不存在,则会自动创建 | 
返回响应:
WATCHING <count>\r\n
conut 是一个数值,指当前 watch list 中有多少 tube 。
ignore
此命令用于从 watch list 中移除一个 tube ,移除之后,该消费者不再接收被移除的 tube 内的 job 。
ignore <tube>\r\n
| 选项 | 描述 | 
|---|---|
| tube | 200 bytes 以内的字符串,代表着 tube 的名字,如果该 tube 不存在,则会自动创建 | 
| 失败响应 | 描述 | 
|---|---|
| NOT_IGNORED\r\n | 如果当前 watch list 中只存在最后一个 tube,则会返回这个响应 | 
成功响应:
WATCHING <count>\r\n
conut 是一个数值,指当前 watch list 中有多少 tube 。
其他命令
peek
用于客户端检查 job ,此命令有四种形态,除了第一个操作以外,其它操作都只针对于当前的 tube 。
peek <id>\r\n 根据 id 返回一个 jobpeek-ready\r\n 返回下一个 ready jobpeek-delayed\r\n 返回下一个剩余延迟时间最短的 delayed jobpeek-buried\r\n 返回下一个 buried job
| 失败响应 | 描述 | 
|---|---|
| NOT_FOUND\r\n | 找不到 job 或没有该状态的 job | 
成功响应:
FOUND <id> <bytes>\r\n<data>\r\n
其中,id 为 job 的 id ,bytes 指 data 的大小(不包含 \r\n ),data 是 job 的具体内容
kick
此命令只适用于当前指定的 tube 。
此命令能将 job 状态改成 ready , 它需要传入一个数字,用于指定需要修改多少个 job 。
比如,你传入 10 ,则会将队列中十个 buried 或 delayed 状态的 job ,修改为 ready 。
如果,指定的队列中存在 buried job ,则只会修改 buried job,否则,就修改 delayed job 。
kick <bound>\r\n
| 选项 | 描述 | 
|---|---|
| bound | 指定要 kick 多少 job | 
响应:
KICKED <count>\r\n
kick-job
这是一个 kick 扩展命令,用于将单独的一个 job 修改为 ready 。它需要传入一个 job id 。
如果传入的 job id 所代表的 job 在当前 tube 中存在,并且该 job 的状态处于 buried 或 delayed ,则会将这个 job 设置为 ready ,并仍然在当前 tube 中。
kick-job <id>\r\n
| 选项 | 描述 | 
|---|---|
| id | job id | 
| 响应 | 描述 | 
|---|---|
| NOT_FOUND\r\n | job 不存在或不处于可 kick 的状态,另外,这也可能发生在内部错误上 | 
| KICKED\r\n | 操作成功 | 
stats-job
此命令用于查看一个 job 的统计信息
stats-job <id>\r\n
| 选项 | 描述 | 
|---|---|
| id | job id | 
| 错误响应 | 描述 | 
|---|---|
| NOT_FOUND\r\n | job 不存在 | 
成功响应:
OK <bytes>\r\n<data>\r\n
bytes 指后面 data 的大小,data 则是该 job 的统计信息,是一个 YAML 格式的文本。
data 包含以下 key :
| key | 描述 | 
|---|---|
| id | job id | 
| tube | 此 job 所在的 tube | 
| state | job 的状态 | 
| pri | job 的优先级 | 
| age | job 在队列中存在的时间,是一个秒数 | 
| time-left | 此 job 距离被放入 ready queue 的剩余秒数,这个时间到达之后,此 job j就会被放入到 ready 队列。此参数只在 job 状态为 reserved 和 delayed 时有意义,当状态为 reserved 时,此参数代表 job 的超时剩余秒数,即 ttr | 
| file | 此 job 的 binlog 序号,如果未开启 binlog ,则此值为 0 | 
| reserved | 此 job 被 reserve 的次数 | 
| timeouts | 此 job 的超时次数 | 
| releases | 此 job 被 released 的次数 | 
| buries | 此 job 被 bury 的次数 | 
| kicks | 被 kicked的次数 | 
stats-tube
此命令返回 tube 的统计信息,如果这个 tube 存在的话。
stats-tube <tube>\r\n
| 选项 | 描述 | 
|---|---|
| tube | 传入 tube 的名称 | 
| 失败响应 | 描述 | 
|---|---|
| NOT_FOUND\r\n | 不存在这个 tube | 
成功响应:
OK <bytes>\r\n<data>\r\n
bytes 是指 data 的大小,不包含 「\r\n」。
data 是一个 YAML 格式的文本,它包含了你想要的 tube 的统计信息
下面是 data 的 key :
| key | 描述 | 
|---|---|
| name | tube 的 name | 
| current-jobs-urgent | 这个 tube 中,优先级小于 1024 的 ready job 数量 | 
| current-jobs-ready | 这个 tube 中的 ready job 数量 | 
| current-jobs-reserved | 这个 tube 中被 reserve 的 job 数量,不论它是被哪个消费者接收的 | 
| current-jobs-delayed | 这个 tube 中处于 delayed 状态的 job 数量 | 
| current-jobs-buried | 这个 tube 中处于 buried 状态的 job 数量 | 
| total-jobs | 此 tube 一共创建过几个 job | 
| current-using | 指向此 tube 的连接数量 | 
| current-waiting | 指向此 tube 并且处于接收等待状态、但还未接收到 job 的连接数量 | 
| current-watching | watch 了此 tube 的连接数量 | 
| pause | 此 tube 停止服务的秒数 | 
| cmd-delete | 此 tube 累计执行了几次 delete | 
| cmd-pause-tube | 此 tube 累计执行了几次 pause-tube | 
| cmd-time-left | 此 tube 几秒之后提供服务 | 
stats
此命令返回整个服务器系统的统计信息。
stats\r\n
成功响应:
OK <bytes>\r\n<data>\r\n
bytes 是指 data 的大小,但不包括 「\r\n」。
data 是一个 YAML 文本,包含了如下 key :
| key | 描述 | 
|---|---|
| current-jobs-urgent | 优先级小于 1024 的 ready job 数量 | 
| current-jobs-ready | ready job 的数量 | 
| current-jobs-reserved | 被接受的 job 数量,不区分客户端(消费者) | 
| current-jobs-delayed | delayed job 的数量 | 
| current-jobs-buried | buried job 的数量 | 
| cmd-put | 累积执行 put 的次数 | 
| cmd-peek | 累积执行 peek 的次数 | 
| cmd-peek-ready | 累积执行 peek-ready 的次数 | 
| cmd-peek-delayed | 累积执行 peek-delayed 的次数 | 
| cmd-peek-buried | 累积执行 peek-buried 的次数 | 
| cmd-reserve | 累积执行 cmd-reserve 的次数 | 
| cmd-use | 累积执行 use 的次数 | 
| cmd-watch | 累积执行 watch 的次数 | 
| cmd-ignore | 累积执行 ignore 的次数 | 
| cmd-delete | 累积执行 delete 的次数 | 
| cmd-release | 累积执行 release 的次数 | 
| cmd-bury | 累积执行 bury 的次数 | 
| cmd-kick | 累积执行 kick 的次数 | 
| cmd-stats | 累积执行 stats 的次数 | 
| cmd-stats-job | 累积执行 stats-job 的次数 | 
| cmd-stats-tube | 累积执行 stats-tube 的次数 | 
| cmd-list-tubes | 累积执行 list-tubes 的次数 | 
| cmd-list-tube-used | 累积执行 list-tube-used 的次数 | 
| cmd-list-tubes-watched | 累积执行 list-tubes-watched 的次数 | 
| cmd-pause-tube | 累积执行 pause-tube 的次数 | 
| job-timeouts | 累积 timeout 的 job 总数 | 
| total-jobs | 累积创建了几个 job | 
| max-job-size | 最大允许的 job 字节数 | 
| current-tubes | 当前有几个 tube | 
| current-connections | 当前有几个连接 | 
| current-producers | 当前有几个至少发出过一条 put 指令的连接 | 
| current-workers | 当前有几个至少发出过一条 reserve 指令的连接 | 
| current-waiting | 当前有几个至少发出过一条 reserve 指令但还未接收到 response 的连接 | 
| total-connections | 累积有过几个连接 | 
| pid | 服务器的进程 id | 
| version | 当前服务器的版本 | 
| rusage-utime | 进程占用用户 cpu 的时间,分别有「秒」和「微秒」的单位 | 
| rusage-stime | 进程占用系统 cpu 的时间,分别有「秒」和「微秒」的单位 | 
| uptime | 此进程已运行的秒数 | 
| binlog-oldest-index | 最早存储的 job binlog 索引号 | 
| binlog-current-index | 当前的 job binlog 索引号,新的 binlog 会从这里开始写入,如果未开启 binlog ,此值为 0 | 
| binlog-max-size | 每个 binlog 文件允许分配的最大容量,单位 bytes | 
| binlog-record-written | 写入 binlog 的累积次数 | 
| binlog-records-migrated | 以压缩形式写入 binlog 的累积次数 | 
| id | 一个随机字符串,用于标记这个进程,在 beanstalkd 开启时生成 | 
| hostname | 主机名,由 uname 决定 | 
上面这些 key 的信息,自从 beanstalkd 启动以来就开始累积,如果重启,就会重新累积。另外,这些数据不存放在 binlog 中
list-tubes
此命令返回所有存在的 tube 。
list-tubes\r\n
成功响应:
OK <bytes>\r\n<data>\r\n
bytes 是指 data 的大小,不包含「\r\n」。
data 返回一个 YAML 字符串,里面包含了 tube 的列表。
list-tube-used
此命令返回当前所 use 的 tube 。
list-tube-used\r\n
成功响应:
USING <tube>\r\n
list-tubes-watched
此命令用于查看当前客户端 watch-list 中的 tube 。
list-tubes-watched\r\n
成功响应:
OK <bytes>\r\n<data>\r\n
bytes 是指 data 的大小,不包含「\r\n」。
data 是一个包含了 tube list 的 YAML 字符串。
quit
此命令用于关闭当前连接。
quit\r\n
pause-tube
此命令为某个 tube 指定一个时间,在这个时间内,此 tube 内的 job 将不会被 reserve 。
pause-tube <tbe-name> <delay>\r\n
| 选项 | 描述 | 
|---|---|
| tube | tube 的名字 | 
| delay | 指定一个秒数 | 
| 响应 | 描述 | 
|---|---|
| PAUSED\r\n | 操作成功 | 
| NOT_FOUND\r\n | 没有这个 tube | 
