Beanstalk 协议

Beanstalk 协议

约定

本文是对beanstalk协议的翻译,原文见这里。 如果在熟悉协议之前想先了解一下beanstalk的基本概念和系统特性,可以参考我之前写的一篇blog。

由于翻译总是会和原文有偏差,所以对一些核心的概念词汇将不予翻译,读者可以仁者见仁,智者见智。这些词汇包括:

beanstalk beanstalkdproducer consumer tube job

有些词汇本文给出了翻译,但只作为参考,在在这里注明:

reserve 获取 release释放 bury 休眠

touch 触碰 peek 窥视 kick 踢

所有的请求和响应都以下面的格式标记:

request response

对于和统计相关的三个命令:stats-job, stats-tube, stats 本文未给出相应的翻译,有想了解的读者请参考原文。

篇幅虽短,但文中难免会有错误,发现了请发送到我的邮箱(qianshi@taobao.com),或者在微博(@淘宝千石)上给我留言,谢谢。

协议说明

Beanstalk协议使用ASCII编码,运行在TCP协议之上。客户端负责主动建立连接,发送命令和数据,等待响应以及关闭连接。对于每个连接,服务端以接收请求的顺序串行地处理命令,并按相同的顺序发送响应。协议中所有的数字都是十进制并且非负的(除非有明确说明)。

协议中的名字都是ASCII字符串。名字可以包含字母(A-Z和a-z)、 数字(0-9)、连字符(”-“)、 加(”+”)、 斜线(”/”)、 分号(”;”)、 点(”.”)、 美元符号(”$”)、下划线(”_”)和括号(“(”和”)”),但是不能以连字符开头。名字以空格或换行结束。每个名字至少要有一个字符的长度。

协议中包含两类数据:文本行和非结构化的块数据。文本行用来传输客户端的命令和服务端的响应。块数据用来传输job体和统计信息。每个job体是一个不透明的字节序列。服务端从不检查或修改job体,直接按其原有的格式返回。job体的解释是有客户端来进行的。

当不再需要使用服务端时,客户端可以选择发送”quit” 命令或直接关闭TCP连接。然而,beanstalkd可以很好处理和维护大量打开的连接,所以,客户端应该尽可能包括连接的打开和重用。这同样也可以省去建立TCP连接的开销。

如果客户端违背了协议(比如发送错误格式的请求或者不存在的命令)或者服务端出错,服务端将返回下面信息的一种:

2 “OUT_OF_MEMORY\r\n” 表示服务端不能为job分配足够的内存。客户端应该稍后重试。

2 “INTERNAL_ERROR\r\n” 表示服务端的bug。这种情况不应该发生。如果发生了,可以报给http://groups.google.com/group/beanstalk-talk

2 “BAD_FORMAT\r\n” 表示客户端发送了一个错误格式的命令行。可能呢导致这种错误发生的情况有:行没有以\r\n结尾、在需要整数的地方出现了非数字字符、参数的数目错误、或者其他格式的命令行错误。

2 “UNKNOWN_COMMAND\r\n” 表示客户端发送了一个服务端不认识的命令。

这些错误响应不会在本文以下各节的命令列出,但都隐含在所有命令的说明里。客户端在发送命令之后都要准备好接收错误响应。

最后,如果服务端发生了严重的错误不能继续服务,服务端将会主动关闭连接。

job生命周期

Beanstalk中的job由客户端发送的 ”put” 命令创建。在整个的生命周期中,一个job可以经历“ready”, “reserved”, “delayed” 和 ”buried” 四种状态。”put”命令之后,一个job通常处于 “ready”状态。它将在ready队列等待worker执行“reserve” 命令。如果这个job是ready队列的下一个,它将由worker来处理。这个worker将执行这个job,当worker完成了该job,可以发送 “delete” 命令来删除job。

下图是一个简单的job生命周期:

下面的图片说明了更复杂的情况:

系统可以有一个或多个tube。每个tube包含一个ready队列和一个delay队列。一个job的生命周期只能在一个tube中。Consumer可以发送 “watch” 命令来关注一个tube,也可以发送 “ignore”命令来忽略一个tube。被关注的tube将会出现在consumer的“watch list”里面。客户端获取的job可能来自其“watch list”中任意一个tube。

当客户端刚建立连接,其“watch list” 中只有初始设置的 “default” tube。如果客户端提交job前没有发送 “use”命令,job将被存储在 “default” tube中。

Tube是在被引用到时按需创建的。如果一个tube空了(也就是说不包含任何ready, delayed 或者buried job)并且没有客户端引用,该tube将被删除。

Producer命令

put

“put” 命令用来向队列中插入一个job,包括一个命令行和一个job体:

put <pri><delay> <ttr> <bytes>\r\n

<data>\r\n

“put” 命令将向客户端现在正在使用的tube中插入一个job(”use” 命令见下文)。

2 <pri> 是一个小于2**32的整数。小优先级数值的job将会排在大优先级数值的job前面执行。最高优先级是0,最低优先级是4,294,967,295。

2 <delay> 是一个整形数,表示将job放入ready队列需要等待的秒数。

2 <ttr> –time to run—是一个整形数,表示允许一个worker执行该job的秒数。这个时间将从一个worker 获取一个job开始计算。如果该worker没能在<ttr>秒内删除、释放或休眠该job,这个job就会超时,服务端会主动释放该job。最小ttr为1。如果客户端设置了0,服务端会默认将其增加到1。

2 <bytes> 是一个整形数,表示job体的大小,不包括结尾的”\r\n”。这个值不能大于max-job-size(默认值为2**16)。

2 <data>及job体,是一个长度为<byetes> 的字符序列。

在发送命令行和job体之后,客户端等待如下的响应:

2 “INSERTED <id>\r\n” 表示成功。<id> 是新job的数字编号。

2 “BURIED <id>\r\n” 表示服务端没有足够的内存将job添加到优先级队列中。<id> 是新job的数字编号。

2 “EXPECTED_CRLF\r\n” 表示该job必须以CR-LF对,及 ”\r\n” 结尾。这两个字节在客户端的put命令中将不会被计算到job大小中。

2 “JOB_TOO_BIG\r\n” 表示客户端发送一个超过max-job-size字节的job体。

2 “DRAINING\r\n” 表示服务端已进入”drain mode”,将不会再接收新的job。客户端应该超时其他的服务端或者断开连接稍后重试。

use

“use” 命令是为producer设计的。put命令会将job放入由该命令指定的tube中。如果没有执行use命令,job将会被放入”default”tube。

use <tube>\r\n

tube是一个不超过200字节的名字。通过名字来指定使用哪个tube。如果该tube不存在,就会被自动创建。

唯一的响应是:

USING <tube>\r\n

<tube>是现在正在使用的tube的名称。

Worker命令

reserve

一个进程可以通过”reserve”, “delete”, “release”和”bury”命令来从队列里面消耗job。”reserve”命令如下:

reserve\r\n

或者,可以指定一个超时时间:

reserve-with-timeout<seconds>\r\n

beanstalkd将一直等待发送响应,直到一个job可用。当一个job被客户端获取,客户端必须在TTR时间内完成该job,否则job就会超时。当该job超时,服务端会将该job重新放回ready队列。TTR和当前剩余时间都可以在stats-job命令的响应中找到。

如果超时设置为0,服务端将立刻返回一个响应或者TIME_OUT。如果超时是一个大于0的值,客户端将会在获取job请求中阻塞,直到一个job可用或者超时。

对于一个已经被获取job的TTR,最后一秒是作为安全边际被服务端保留的,在此期间,客户端将无法试图去等待别的job。如果客户端在安全边际期间发送获取job命令,或者安全边际到来时还在等待获取命令的完成,服务端将返回:

DEADLINE_SOON\r\n

这给了客户端一个在服务端自动发布该job之前,删除或者重新发布已经获取job的机会。

TIMED_OUT\r\n

当指定了一个非负的超时时间值,并且在超时时或没有一个job可用,服务端将返回TIMED_OUT。

否则,这条命令唯一成功的响应形式是一个文本行后跟一个job体。

RESERVED <id><bytes>\r\n

<data>\r\n

2 <id> job的id,在这个beanstalkd的实例中,代表该job的唯一整数。

2 <bytes>表示job体大小的整数,不包括结尾的 ”\r\n”。

2 <data>表示消息体,一个长度为<bytes> 的字节序列。这是对之前put命令job体原始字节的逐字拷贝。

delete

delete命令从服务端完全删除一个job。当客户端已经成功执行完一个job时,该job一般会被使用。客户端可以删除一个已经被获取的job,可用的job,以及被休眠的job。delete命令如下:

delete <id>\r\n

<id> 表示要删除的job id。

客户端将等待如下的响应行:

2 “DELETED\r\n” 表示已经成功删除。

2 “NOT_FOUND\r\n” 表示该job不存在,或者该job没有被客户端获取,已经不在ready或buried状态。这种情况发生这客户端发送delete命令之前,该job已经超时。

release

release命令将一个已经被获取的job重新放回ready队列(并将job状态置为 “ready”),让该job可以被其他客户端执行。这个命令经常在job因为短暂的错误而失败时使用。格式如下:

release <id><pri> <delay>\r\n

2 <id> 表示要release的job id。

2 <pri> 表示给该job分配的新的优先级。

2 <delay>表示在该job被放入ready队列之前需要等待的秒数。在此期间,job的状态将是”delayed”。

客户端期待的响应如下:

2 “RELEASED\r\n” 表示成功

2 “BURIED\r\n” 表示服务端没有足够的内存将job添加到优先级队列中。

2 “NOT_FOUND\r\n” 表示该job不存在或没有被客户端获取。

bury

bury命令将一个job的状态置为”buried”。Buried job被放在一个FIFO的链表中,在客户端调用kick命令之前,这些job将不会被服务端处理。

bury命令格式如下:

bury <id><pri>\r\n

2 <id> 表示该job的id

2 <pri>表示分配给该job新的优先级

有两种可能的响应:

2 “BURIED\r\n” 表示成功

2 “NOT_FOUND\r\n” 表示该job不存在或没有被客户端获取。

touch

touch命令允许一个worker请求在一个job获取更多执行的时间。这对于那些需要长时间完成的job是非常有用的,但同时也可能利用TTR的优势将一个job从一个无法完成工作的worker处移走。一个worker可以通过该命令来告诉服务端它还在执行该job(比如:在收到DEADLINE_SOON是可以发生给命令)。

touch命令格式如下:

touch <id>\r\n

2 <id>表示当前连接获取job的id

有两种可能的响应:

2 ” TOUCHED\r\n” 表示成功

2 “NOT_FOUND\r\n” 表示该job不存在或没有被客户端获取。

watch

watch命令将一个tube名称加入当前连接的watch list。reserve命令将从watch list里面的任意一个tube中获取job。对于每个新建的连接,watch list中只有一个”default” tube。

watch <tube>\r\n

2 <tube>是一个不超过200字节的名称。它指定将一个tube加入到watch list。如果该tube不存在,将会被及时创建。

响应是:

WATCHING <count>\r\n

2 <count>表示当前watch list中的tube数目。

ignore

ignore命令是为consumer设计的。ignore命令将一个tube从当前连接的watch list中移除。

ignore <tube>\r\n

响应如下:

2 “WATCHING <count>\r\n” 表示成功。<count>表示当前watchlist中的tube数目。

2 “NOT_IGNORED\r\n” 表示客户端正在试图ignore watch list中的最后一个tube。

其他命令

peek

peek命令可以让客户端检查系统中的job。这个命令有4个变种。除第一个命令之外,其他3个命令都只能作用在当前使用的tube上面。

“peek<id>\r\n” 返回编号为<id>的job。

“peek-ready\r\n”返回当前tube下一个ready的job。

“peek-delayed\r\n”返回当前tube剩余delay时间最短的job。

“peek-buried\r\n”返回当前tube buried list中下一个job。

有两种可能的响应,其中一个是单行:

“NOT_FOUND\r\n” 表示请求的job不存在,或者没有job在当前请求的状态队列中。

还有一个是一行跟一个块数据,表示命令执行成功:

FOUND <id><bytes>\r\n

<data>\r\n

2 <id> 表示job的id。

2 <bytes> 表示job体大小的整数,不包括结尾的 ”\r\n”。

2 <data>表示消息体,一个长度为<bytes> 的字节序列。

kick

kick命令只能针对当前正在使用的tube执行。它将buried或者delayed状态的job移动到ready队列。命令格式如下:

kick <bound>\r\n

2 <bound>表示每次kick job的上限,服务端将最多kick <bound>个job。

响应格式如下:

KICKED <count>\r\n

2 <count>表示本次kick操作作用job的数目。

list-tubes

list-tubes命令返回已经存在的所有tube的列表。格式如下:

list-tubes\r\n

响应如下:

OK <bytes>\r\n

<data>\r\n

2 <bytes> 是<data>的字节大小。

2 <data>是一个长度为<bytes> 的字节序列。它是一个包含所有tube 名称的YAML文件。

list-tube-used

list-tube-used命令返回客户当前正在使用的tube。格式如下:

list-tube-used\r\n

响应如下:

USING <tube>\r\n

2 <tube>是正在使用的tube 名称。

list-tubes-watched

list-tubes-watched命令返回客户端当前正在关注的tube名称列表。格式如下:

list-tubes-watched\r\n

响应如下:

OK <bytes>\r\n

<data>\r\n

2 <bytes> 是<data>的字节大小。

2 <data>是一个长度为<bytes> 的字节序列。它是一个包含被关注tube 名称的YAML文件。

pause-tube

pause-tube命令用来在给定时间内暂停从tube获取job。格式如下:

pause-tube<tube-name> <delay>\r\n

2 <tube> 表示要暂停的tube。

2 <delay>表示在可以从tube获取job之前需要等待的秒数。

有两种可能的响应:

“PAUSED\r\n” 表示成功。

“NOT_FOUND\r\n” 表示该tube不存在。

quit

quit命令用来关闭当前连接。格式如下: quit\r\n

欢迎关注下方“非著名资深码农“公众号进行交流~

发表评论

邮箱地址不会被公开。