MooseFS 是运用很广泛的一个分布式文件系统,其自带有一个 python 写的 CGI 页面,可以查看集群状态。不过对于运维来说,这就不太方便纳入 nagios 等其他现有的监控体系中。好在既然它的 CGI 是 python 写的,那么自己照样临摹出一个监控脚本也不是太复杂。
其实整个数据是由 master 的 9421 端口进行 TCP 交互获取的,不过比较麻烦的是并不是普通文本流。CGI 中采用了 pack/unpack 函数来处理 TCP 包。根据数据的前 8 字节确定数据总长度和 MooseFS 的版本,然后依照不同版本的 pack 方式来 unpack 剩余内容。
笔者熟悉 Perl,所以就准备将这个处理流程改用 Perl 完成。结果发现原来 pack/unpack 在 Perl 和 Python 中,写法是不一样的。以 MooseFS 的 info 信息读取代码为例,Python 版如下:
s = socket.socket()
s.connect((masterhost, masterport))
mysend(s, struct.pack(">LL", 510, 0))
header = myrecv(s, 8)
cmd, length = struct.unpack(">LL", header)
if cmd == 511 and length == 76:
data = myrecv(s, length)
v1, v2, v3, memusage, total, avail, trspace, trfiles, respace, refiles, nodes, dirs, files, chunks, allcopies, tdcopies = struct.unpack(">HBBQQQQLQLLLLLLL", data)
ver = '.'.join([str(v1), str(v2), str(v3)])
而 Perl 版最终写完是这样的:
my $s = IO::Socket::INET->new(
PeerAddr => $host,
PeerPort => $port,
Proto => 'tcp',
);
my ($header, $data);
print $s pack('(LL)>', 510, 0);
sysread $s, $header, 8;
my ($cmd, $length) = unpack('(LL)>', $header);
if ( $cmd == 511 and $length == 76 ) {
sysread $s, $data, $length;
my ($v1, $v2, $v3, $memusage, $total, $avail, $trspace, $trfiles, $respace, $refiles, $nodes, $dirs, $files, $chunks, $allcopies, $tdcopies) = unpack('(SCCQQQQLQLLLLLLL)>', $data);
my $ver = "$v1.$v2.$v3";
};
不同处主要有两点:
big-endian
定义的 >
符号位置不同,Python 里写在起首一次性全部生效;Perl 里需要每个格式符单独定义,或者采用括号合起来总定义;H
格式符表示 unsigned short
,在 Perl 里应该是 S
;Python 里的 B
格式符表示 unsigned char
,在 Perl 里应该是 C
。翻看了一下,在 PHP 和 Ruby 中,格式符定义和 Perl 是一样的,不清楚为什么 Python 这么特殊==!
各语言关于 pack
格式符的文档链接如下: