| 2014年5月4日
使用sawzall开发mr程序虽然很快,不过也有不少限制,尤其对于新手来说,比如不能直接像c++一样调用线程的库,不过它提供了一种扩展开发的方式,我们可以自己修改sawzall代码实现新的功能接口,在下载平台的sawzall代码中,我们就扩展了一种聚合器,两个和我们web相关的功能接口:连接我们的domain service进行聚合域名的识别,根据url进行域名提取。这里以使用我们comm库中的url类进行域名提取为例来说明一下sawzall的功能函数扩展。
首先来看一下sawzall代码的目录结构和其用途说明
Szl的src目录下有如下一些文件夹: app:包含szl本地化工具的相关实现,
app/tests/目录下还包含了一个MapReduce的实现实例
contrib:内含Emacs的一个插件
emitters:一系列聚合器实现
emitvalues:sawzall相关一些基本类型定义,包括decoder,emitter,encoder,tableentry等
engine:sawzall语言相关,包括词法分析,语法分析,执行引擎
fmt:格式化输出相关
intrinsics:一些常见的运算支持,也是功能扩展接口,比如压缩,排序,数据额函数等
protoc_plugin:protobuf相关工具
public:公开的头文件
utilities:基本工具类
我在实现的扩展功能就是从参考intrinsics目录下的其它方法来实现的,例如我添加了一个新文件:urldomainintrinsics.cc ,他的namespace仍然是namespace sawzall。
现在文件末尾添加一个函数:
REGISTER_MODULE_INITIALIZER(
UrlDomainIntrinsic,
{REQUIRE_MODULE_INITIALIZED(Sawzall); sawzall::Initialize();}
);
REGISTER_MODULE_INITIALIZER是向sawzall中注册钩子函数,让zawzall代码在启动的时候能够执行这里面的Inintialize函数,从而能把我们写的扩展函数暴露出来。每个扩展文件都要注册这个函数,这里再看看这个sawzall::Initialize()函数,因为这个宏是在代码的namespace sawzall外面执行的所以添加sawzall::。
static void Initialize() {
// make sure the SymbolTable is initialized
assert(SymbolTable::is_initialized());
Proc* proc = Proc::initial_proc();
// shortcuts for predefined types
Type* bool_type = SymbolTable::bool_type();
Type* float_type = SymbolTable::float_type();
Type* string_type = SymbolTable::string_type(); // 这里注册了一个函数 get_host_port,参数是string类型的
SymbolTable::RegisterIntrinsic("get_host_port",
FunctionType::New(proc)->
par("get_host_port", string_type)->
res(string_type),
get_host_port,
get_host_port_doc, Intrinsic::kNormal);
}
第一个参数是制定调用名称,第二个是制定调用的函数的参数类型,第三个是函数的具体实现,第四个是函数的一个说明,其实就是一个字符串,第五个参数是说明这个函数的类型,这个类型有以下三种:
enum Attribute
{
kNormal = 0 << 0,
kCanFold = 1 << 0, // This intrinsic can be constant-folded.
kThreadSafe = 1 << 1, // This intrinsic is thread-safe.
};
再来看看函数的实现:
static const char get_host_port_doc[] = "Return host port of url"; /// 函数说明
static void get_host_port(Proc* proc, Val**& sp)
{
string original_url = Engine::pop_cpp_string(proc, sp); /// 这里从sawzall上层获取传入的参数
/// 这里开始处理参数,使用外部类接口
char* str_domain = NULL;
StringVal* szl_string = NULL;
static web::url::Url url;
url.Load(original_url.c_str(), original_url.length());
if (url.IsValid()) /// url类的有效判断
{
szl_string = Factory::NewStringCPP(proc, url.GetHostPort());
/// 装换为sawzall的string类型,它针对string和char有不同的函数来处理
}
else
{
szl_string = Factory::NewStringC(proc, "bad.site");
}
Engine::push(sp, szl_string); ///这里再把这个处理后的结果放回sawzall上层
}
这样扩展之后就可以在sawzall代码中直接调用get_host_port这个函数了。
例如我的sawzall代码是这样写的:
g_processor_map = function(data: bytes): int {
#get glable data from pb
log := falcon.DispatchServerCrawlResultLog(data);
url := string(log.request_log.url);
site := get_host_port(url); #在这里使用这个函数直接获取url对应的域名
。。。。。。
};
好了,总结记录到这里,此外我们还扩展了domian求聚合域名的方法,还扩展了sawzall的一个聚合器来输出同一个key下的多个value。这些以后再写出来。
关注「黑光技术」,关注大数据+微服务