19.4.2 即刻Hadoop应用架构
图19-5是即刻搜索引擎的应用架构图。
即刻搜索框架结构比较明了。下面我们对框架各个角色进行介绍。
链接库:这是一个保存了网络中内外部链接的初始数据库,即刻搜索根据数据库中的链接进行网页的爬取。
即刻爬虫:网络爬虫是搜索引擎中必不可少的部分,即刻爬虫根据链接库的内容,爬取网络中的页面资源,形成SSTable并输入HDFS中。SSTable是Google在BigTable设计中提出的一种磁盘文件存储结构,全称是Sorted String Table,以<key, value>对方式在磁盘上存储数据,并根据key的值进行排序,支持随机查找,有不俗的读写性能。
HDFS_Bridge:这是即刻搜索的中间件,为网络爬虫提供写缓存服务,保证爬虫快速写操作。具体来说,通过HDFS_Bridge,爬虫生成的SSTable文件,首先以内存写的速度将数据写入HDFS_Bridge,然后由DFS直接将数据文件写出到DFS磁盘上,这样通过HDFS_Bridge就将SSTable数据的磁盘I/O转化成了内存写,提高了速度。
HDFS:即刻搜索中保存SSTable的分布式文件系统,主要提供海量数据的存储服务,保证数据的安全性和读写服务的可靠性。
MapReduce:这一层主要应用Hadoop中的MapReduce并行编程框架来对爬虫原始数据进行分析,包括PageRank计算、链接分析统计、倒排索引生成等,主要提高了搜索引擎中数据分析步骤的速度,实现了并行化处理。
online-service-cluster:这一层是面向用户的,主要根据用户输入的查询,分析关键词,通过并行框架查找相关结果并返回。
图 19-5 即刻搜索架构图
这里再重点介绍一下MapReduce层。在即刻搜索中,各种基础数据上的操作都是以C++语言编写、经过Hadoop Pipes的封装之后提交给Hadoop执行的,但是具体使用中即刻搜索也从代码层修改了Hadoop Pipes的协议等内容来适应自己的需求。在具体使用中,即刻搜索首先定义了MapReduce框架中的Mapper封装器和Reducer封装器,以Mapper封装器为例,其核心代码如下:
Wrapperclass BasicMapper{
public:
typedef:mapreduce:TaskContextTaskContext;
explicit BasicMapper(){
mapcontext.reset(new MapContext());
}
virtual~BasicMapper(){}
//初始化操作
virtual void OnCreate(TaskContext*context){}
//定义Map阶段
virtual void OnFirstMap(MapContext*context){}
virtual void OnLastMap(MapContext*context){}
virtual void Map(MapContext*context)=0;
……
protected:
//获取输入value
const std:string&GetInputValue(){
return mapcontext->GetInputValue();
}
//获取输入key
const std:string&GetInputkey(){
return mapcontext->GetInputKey();
}
//反序列化
template<typenameT>
boolValueToThrift(T*object){
return mapcontext->ValueToThrift(object);
}
……
DISALLOW_COPY_AND_ASSIGN(BasicMapper);
}
利用这些封装器作为基类,编写自己的MapReduce框架代码就非常方便。下面就是一个简单的例子:
class SampleMap:public BasicMapper{//Mapper
public:
virtual~SampleMap(){}
virtual void Map(mapreduce:MapContext*context)
{
string key=GetInputKey();
string value=GetInputValue();
Object obj_val;
ValueToThrift(&obj_val);//反序列化
……
context->Emit(key, newvalue);//输出
}
};
class SampleRed:public BasicReducer{
public:
virtual void Reduce(mapreduce:MapContext*context){
string key=GetInputKey();
while(NextValue()){
string value=GetInputValue();
……
}
context->Emit(key, newvalue);//输出
}
};
可以看出:上面的代码同用Java语言编写的代码非常类似。除了上面的主体Mapper和Reducer代码之外,再定义好其他作业信息就可以提交给Hadoop Pipes来运行了。