就像上一篇博文中说的,其实最大的障碍在于hadoop自带的pipes静态库和动态库都是为linux平台的,而不是为MacOS平台的。在MacOS下,想要使用pipes,需要重新编译库文件。编译过程和方法见上一篇博文。
其他的,似乎没有太多好说的。我就列出代码吧。
hadoopWordCountPipe.cpp的内容如下:
// the header files of haddop #include "hadoop/Pipes.hh" #include "hadoop/TemplateFactory.hh" #include "hadoop/StringUtils.hh" #include <string> #include <vector> using namespace std; const string WORDCOUNT = "WORDCOUNT"; const string INPUT_WORDS = "INPUT_WORDS"; const string OUTPUT_WORDS = "OUTPUT_WORDS"; class WordCountMap: public HadoopPipes::Mapper { public: HadoopPipes::TaskContext::Counter * inputWords; WordCountMap (HadoopPipes::TaskContext & context) { inputWords = context.getCounter(WORDCOUNT, INPUT_WORDS); } void map (HadoopPipes::MapContext & context) { vector<string> WordVec = HadoopUtils::splitString (context.getInputValue(), " "); for (int i=0; i<(int)WordVec.size(); i++) context.emit (WordVec.at(i), "1"); context.incrementCounter (inputWords, WordVec.size()); } }; class WordCountReduce: public HadoopPipes::Reducer { public: HadoopPipes::TaskContext::Counter * outputWords; WordCountReduce (HadoopPipes::TaskContext & context) { outputWords = context.getCounter (WORDCOUNT, OUTPUT_WORDS); } void reduce (HadoopPipes::ReduceContext & context) { int sum = 0; while (context.nextValue()) { sum += HadoopUtils::toInt (context.getInputValue()); } context.emit (context.getInputKey(), HadoopUtils::toString(sum)); context.incrementCounter (outputWords, 1); } }; int main (int argc, char * argv[]) { return HadoopPipes::runTask (HadoopPipes::TemplateFactory<WordCountMap, WordCountReduce>()); }
还是说一下这个代码。对比《【hadoop学习】在伪分布式hadoop上手把手实践word count程序【上】》中的java代码,可以看到有一些相同的地方,也有一些不同的地方:
- 相同的是,都有map和reduce类,并且类中都要实现map和reduce函数;不同的是,java的类外面还套着类,通常作为jar包的名字。而pipes中c++程序,没有那么多嵌套。
- 不同的是,java native的hadoop程序,可以指定map和reduce的输入对儿和输出的类型,类型通常可以是Text或者LongWritable,对应字符串和整数。而pipes中,输入和输出仅仅局限于字符串类型,即便是整数值,也是以字符串进行输入和输出,然后通过函数(例如:HadoopUtils::toInt)进行转化。
- 不同的是,java native的hadoop程序,在map和reduce类中load资源或者初始化容器,都要在setup函数中。而pipes中,直接在c++的构造函数中了。
- 在pipes上述代码中,明确在构造函数中实现了counter。关于counter更多的知识,请参考《hadoop权威指南》第八章的内容。
Makefile的内容如下:
HADOOP_INSTALL="/Volumes/Data/Works/Hadoop/hadoop-0.20.2" PLATFORM=Mac_OS_X-x86_64-64 CC = g++ CPPFLAGS = -m64 -I$(HADOOP_INSTALL)/c++/$(PLATFORM)/include hadoopWordCountPipe: hadoopWordCountPipe.cpp $(CC) $(CPPFLAGS) $< -Wall -L$(HADOOP_INSTALL)/c++/$(PLATFORM)/lib -lhadooppipes -lhadooputils -lpthread -g -O2 -o $@
注意PLATFORM变量的设置,不能是Linux-i386-32,而是新编译出来的Mac_OS_X-x86_64-64。并且CPPFLAGS中要设置"-m64"而不是"-m32",表示是编译成64位的程序。
为了在hadoop上运行,还需要一个配置文件(直接用命令行也行,不过敲起来比较麻烦)。配置文件hadoopWordCountPipeConf的内容如下:
<?xml version="1.0"?> <configuration> <property> <name>hadoop.pipes.executable</name> <value>bin/hadoopWordCountPipe</value> </property> <property> <name>hadoop.pipes.java.recordreader</name> <value>true</value> </property> <property> <name>hadoop.pipes.java.recordwriter</name> <value>true</value> </property> </configuration>
其中第一个属性是编译好的c/c++二进制文件hadoopWordCountPipe在hdfs中的位置。我还没有尝试用本地路径是否work。不过在运行前还是put到了hdfs上。接下来,在终端敲命令:
bin/hadoop pipes -conf /Volumes/Data/Works/hadoopWordCountPipe/hadoopWordCountPipeConf -input input -output outputPipes
主要是指定conf文件和输入输出路径。
hadoop运行并输出信息
12/10/23 22:18:14 WARN mapred.JobClient: No job jar file set. User classes may not be found. See JobConf(Class) or JobConf#setJar(String). 12/10/23 22:18:14 INFO mapred.FileInputFormat: Total input paths to process : 1 12/10/23 22:18:15 INFO mapred.JobClient: Running job: job_201210232200_0002 12/10/23 22:18:16 INFO mapred.JobClient: map 0% reduce 0% 12/10/23 22:18:30 INFO mapred.JobClient: map 100% reduce 0% 12/10/23 22:18:42 INFO mapred.JobClient: map 100% reduce 33% 12/10/23 22:18:45 INFO mapred.JobClient: map 100% reduce 100% 12/10/23 22:18:53 INFO mapred.JobClient: Job complete: job_201210232200_0002 12/10/23 22:18:53 INFO mapred.JobClient: Counters: 20 12/10/23 22:18:53 INFO mapred.JobClient: WORDCOUNT 12/10/23 22:18:53 INFO mapred.JobClient: OUTPUT_WORDS=125 12/10/23 22:18:53 INFO mapred.JobClient: INPUT_WORDS=460299 12/10/23 22:18:53 INFO mapred.JobClient: Job Counters 12/10/23 22:18:53 INFO mapred.JobClient: Launched reduce tasks=1 12/10/23 22:18:53 INFO mapred.JobClient: Launched map tasks=2 12/10/23 22:18:53 INFO mapred.JobClient: Data-local map tasks=2 12/10/23 22:18:53 INFO mapred.JobClient: FileSystemCounters 12/10/23 22:18:53 INFO mapred.JobClient: FILE_BYTES_READ=4017857 12/10/23 22:18:53 INFO mapred.JobClient: HDFS_BYTES_READ=2209727 12/10/23 22:18:53 INFO mapred.JobClient: FILE_BYTES_WRITTEN=8035784 12/10/23 22:18:53 INFO mapred.JobClient: HDFS_BYTES_WRITTEN=1164 12/10/23 22:18:53 INFO mapred.JobClient: Map-Reduce Framework 12/10/23 22:18:53 INFO mapred.JobClient: Reduce input groups=125 12/10/23 22:18:53 INFO mapred.JobClient: Combine output records=0 12/10/23 22:18:53 INFO mapred.JobClient: Map input records=30956 12/10/23 22:18:53 INFO mapred.JobClient: Reduce shuffle bytes=4017863 12/10/23 22:18:53 INFO mapred.JobClient: Reduce output records=125 12/10/23 22:18:53 INFO mapred.JobClient: Spilled Records=920598 12/10/23 22:18:53 INFO mapred.JobClient: Map output bytes=3097253 12/10/23 22:18:53 INFO mapred.JobClient: Map input bytes=2207611 12/10/23 22:18:53 INFO mapred.JobClient: Combine input records=0 12/10/23 22:18:53 INFO mapred.JobClient: Map output records=460299 12/10/23 22:18:53 INFO mapred.JobClient: Reduce input records=460299
在输出目录中查看结果文件内容,如下:
-1 23510 1 7446 100:1 53 101:1 79 102:1 64 103:1 613 104:1 36 105:1 23 106:1 28 107:1 67 108:1 17 109:1 26 10:1 1978 110:1 47 111:1 44 112:1 56 113:1 12 114:1 58 115:1 33 116:1 12 117:1 16 118:1 15
可以看到,内容与java版本运行出的结果一致。(参考《【hadoop学习】在伪分布式hadoop上手把手实践word count程序【下】》)。