2

最近工作上需要写一个小工具,用于解析 beanstalkd 的 binlog。一如既往,我先在网上搜索相关的资料。令我惊讶的是,beanstalkd 官方文档中连关于 binlog 格式的只言片语都没有。网络上跟 beanstalkd binlog 格式相关的资料也几乎是个空白。所以在阅读了 beanstalkd 相关源码,并编写了对应工具之后,我试着在本文记下 beanstalkd 的 binlog 的一些相关信息。

binlog 文件格式

beanstalkd 的 binlog 位于 -b 选项指定的文件夹中,名字就叫 binlog.$order。binlog 的序号 $order 从 1 开始,逐一递增。
binlog 文件大小是固定的,可以通过 -s 选项指定,默认为 10M。beanstalkd 在创建 binlog 文件时,会先用 0 填充文件的全部内容。

beanstalkd 的 binlog 内容非常浅显直白。每个 binlog 文件最开始是一个 int 类型的版本号,表示 binlog 版本号。
从 2013 年至今,最新的版本号是 7。可以这么认为,现行见到的 binlog 版本号都是 7。之后是每个操作的 binlog 记录。
值得一提的是,这里的 int 的大小是由编译时编译器决定的,虽然在主流平台上它的值是 32 位的。在解析时,最好用 int32 或等价操作去
处理它,而不是照搬 int 的定义。比如像 golang 里面的 int 就是默认 64 位的。

每个操作的 binlog 记录由四部份组成:

  1. len(tubeName),大小为 int
  2. 可选的 tubeName
  3. 表示 job 元数据的 record
  4. 可选的 job body 内容

job record 的格式如下:

// 注意我把 padding 标记成 _,原来的结构体定义里面是没有这个符号的
struct Jobrec {
    uint64 id;          // id >= 1
    uint32 pri;
    uint32 _;           // 注意这里有一个内存对齐导致的 padding
    int64  delay;       // 精确到纳秒
    int64  ttr;         // 精确到纳秒
    int32  body_size;
    int32  _;           // 这里是另外一个内存对齐导致的 padding
    int64  created_at;  // 创建时间, epoch 纪年,精确到纳秒
    int64  deadline_at; // 下一个会因超时而产生状态变迁的时间
    uint32 reserve_ct;  // reserve 状态切换计数,_ct 结尾的都是状态计数
    uint32 timeout_ct;
    uint32 release_ct;
    uint32 bury_ct;
    uint32 kick_ct;
    byte   state;
    byte[3] _;          // 又一个 padding
};

创建的操作,会进行 write job full。也即会写入 len(tubeName),tubeName,record,body 这四项记录。
其他操作,会进行 write job short。只写入 0,record 这两项记录。这是因为 record 里面已经存有 id 了,
可以通过 id 获取对应 job 的 tubeName 和 body,所以就不需要在 binlog 里重复记录。

record 中的 state 取值如下:

enum // Jobrec.state
{
    Invalid,
    Ready,
    Reserved,
    Buried,
    Delayed,
    Copy
};

Ready, Reserved, Buried, Delayed 这四种状态对应的是 beanstalkd protocol
里面 job 的四种状态。
Invalid 对应的是删除特定的 job 的操作。
Copy 是 stats*peek* 系列命令处理过程中产生的临时 job 才有的状态,不会记录到 binlog 中。

当我们解析到 id 为 0 的 job 时,意味着该 binlog 已经全部解析完毕,可以开始解析下一个 binlog 文件了。

总之解析每个 binlog 文件的流程是这样的:
binlog解析过程

binlog GC 和 compat

beanstalkd 会不停地产生 binlog 记录,如果缺少清理机制,很快就会把磁盘给塞满。
beanstalkd 在内部维护每一个 binlog 文件的 ref 计数。当一个文件里进行 write job full 操作时,会增加 ref;当一个 job
被删除时,会减少对应文件的 ref。当一个文件的 ref 减至 0,就可以放心地把它删掉了。这一操作,类似于 GC 中的引用计数,
在 beanstalkd 内部实现中也是把它称作 GC。

上述机制有一个问题。如果有个 job 一直不能够被删除掉,则对应的 binlog 文件也会一直无法删除掉。受影响的不止单个文件。由于
beanstalkd 需要保证 binlog 文件的有序性,该文件之后的其他 binlog 也无法被删除掉。

因此 beanstalkd 引入了 compat 机制来解决这个问题。
如果实际使用量不及日志量的一半,beanstalkd 会开始进行 compat。compat 不会修改现有 job,但会减少 job 对应文件的 ref,并新写入一条记录。
其效果等价于,把一个旧的 job 操作记录移动到新的文件中来。这么一来,就可以避免旧文件的回收被阻塞的情况了。


spacewander
5.6k 声望1.5k 粉丝

make building blocks that people can understand and use easily, and people will work together to solve the very largest problems.


引用和评论

0 条评论