15 Nov 2013
Apache Thrift的一另类用法 - dump/load数据文件
Apache Thrift 一般被用做跨语言的服务的开发。它在这方面很好用,高效且方便,我现在服务的美团大量的使用了它。
最近在做Deal的推荐系统,需要加载Deal的详细信息到内存。修改代码到程序跑起来的时间长短影响着开发效率,当然是越快越好,不希望每次修改代码后,编译,重启都需要去向数据库要一遍所有Deal的信息,毕竟C++的编译已经很耗时(这是我喜欢go的一个原因,它编译迅速。但go对需要加载几百万用户的上亿行为到内存,20ms左右算出推荐结果的场景有些力不从心)。一个可行的办法是把Deal信息dump到本地文件,重启时,快速的load这个文件。
Thrift的一个功能是把数据按照预定义的protocol,dump成与程序语言无关bytes,通过网络传输给另外的进程,对方以同样的protocol,load这些bytes,还原为原来的数据。通过网络这部分可以换成文件。
简单示例:定义数据格式,生成code
// Thrift 定义文件 data.thrift
struct DealTiny {
1: required i32 dealid,
2: required i32 classid,
3: required i32 mttypeid,
4: required i32 bizacctid,
5: required bool isonline,
6: required i32 geocnt,
}
struct DealsTiny {
1: required list<DealTiny> deals
}
通过下面的命令,生成需要的c++,和py code
# 生成py的code。python从数据库load数据,并保持为文件
thrift -gen py data.thrift
# 生成c++的code。线上服务是c++写的,它需要load py 生成的数据文件
thrift -gen cpp data.thrift
dump 数据文件的python code 片段
def dump_deals():
deals = DealsTiny()
# 从db load数据
# 用Thrift dump deals为bytes
itransport = TTransport.TMemoryBuffer()
prof = TBinaryProtocol.TBinaryProtocolAcceleratedFactory()
ipro = prof.getProtocol(itransport)
deals.write(ipro)
# 写入文件
buf = itransport.getvalue()
with open("deals_info.bin", 'w') as f:
f.write(buf)
load 数据文件的C++ code 片段
int load_deals(std::string file, DealsTiny &deals) {
// mmap文件到内存
int fd = open(file.c_str(), O_RDONLY);
if (fd < 0) {
perror(file.c_str());
return fd;
}
const long size = get_file_size(file);
unsigned char *buffer = (unsigned char*)mmap(NULL, size, PROT_READ, MAP_PRIVATE | MAP_POPULATE, fd, 0);
close(fd);
if (buffer == MAP_FAILED) {
perror("mmap");
return -1;
}
// 用Thrift load数据文件。
shared_ptr<TTransport> itransport(new TMemoryBuffer(buffer, size));
TBinaryProtocol ipro(itransport);
deals.read(&ipro);
munmap(buffer, size);
return 1;
}
Thrift的BinaryProtocol很高效,用这种方式load数据文件方便,且快,很喜欢。