由于工作需要,要在Hadoop中使用C++的代码,故开始研究在hadoop中使用Java本地接口,实现流程如下:
1、使用java封装C++接口;
2、使用C++实现该接口,并编译生成动态库;
3、将第1步中编写的java代码封装成jar包;
4、将封装好的jar包、C++的动态库拷贝到hadoop中;
5、编写mapreduce测试程序进行测试
1、使用java封装C++接口,代码如下:
package org.jni;
public class JNITest {
public static native String PrintLine (String line);
static
{
System.loadLibrary("JNITest");
}
}
编译生成class文件,在class文件的基础上,用javah命令生产C++的头文件,命令如下:
javah -jni org.jni.JNITest
(注:可能会报错找不到类文件)这是因为需要设置CLASSPATH环境变量,例如class文件所在目录为/root/code/workspace/JNITest/bin/org/jni/JNITest.class,可以将环境变量CLASSPATH设置为/root/code/workspace/JNITest/bin/
环境变量设置好后命令就可以执行成功了,执行成功后会生成org_jni_JNITest.h,接下来就可以编写C++程序实现这个函数了。
2、使用C++实现该接口,并编译生成动态库,由于只是测试,只实现了一个简单的输出字符串功能;
编写C++的源文件org_jni_JNITest.cpp,的代码如下:
#include <jni.h>
#include <stdio.h>
#include <string.h>
#include "org_jni_JNITest.h"
JNIEXPORT jstring JNICALL Java_org_jni_JNITest_PrintLine
(JNIEnv *env, jclass obj, jstring line)
{
char buf[128];
const char *str = NULL;
str = env->GetStringUTFChars(line, false);
if (str == NULL)
return NULL;
strcpy (buf, str);
env->ReleaseStringUTFChars(line, str);
return env->NewStringUTF(buf);
}
编写好之后,进行编译生成C++动态库libJNITest.so,编译命令如下:
g++ -I /usr/lib/jdk/jdk1.7.0_15/include/ -I /usr/lib/jdk/jdk1.7.0_15/include/linux/ org_jni_JNITest.cpp -fPIC -shared -o libJNITest.so
编译成功会生成libJNITest.so文件。
3、将第1步中编写的java代码封装成jar包,这个就不需要说了吧。
4、将封装好的jar包、C++的动态库拷贝到hadoop中。
将封装好的jar包拷贝到$HADOOP_HOME/lib/目录下,将libJNITest.so拷贝到$HADOOP_HOME/lib/native/Linux-amd64-64目录下(如果是32位的系统就拷贝到$HADOOP_HOME/lib/native/Linux-i386-32目录下),下面Hadoop就可以使用我们封装好后的C++函数了。
5、编写mapreduce测试程序进行测试。
在编写Mapreduce程序进行测试之前,我修改了org.apache.hadoop.mapreduce.Mapper的源码,只是加入了一个成员变量,代码如下:
只加入两行代码,首先需要引入我们封装好的jar包,在Mapper中增加了一个常量并为其赋值,这里里用到了我们之前封装好的C++函数,修改好源码之后对其进行编译生成hadoop-core-1.2.0.jar(编译的方法直接Google吧),用hadoop-core-1.2.0.jar替换$HADOOP_HOME/目录下的hadoop-core-1.2.0.jar,下面开始编写Mapreduce程序。
mapreduce程序非常简单,只是从一个文本中读取每一行,并将每一行设置为key,至于value就设置为上文在Mapper这个类中加入的成员变量的值,程序如下:
package org.test.mapreduce;
import java.io.IOException;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.conf.Configured;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.apache.hadoop.util.Tool;
import org.apache.hadoop.util.ToolRunner;
public class JNIMapreduce extends Configured implements Tool {
static class JNIMap extends Mapper<LongWritable, Text, Text, Text> {
String str = "";
@Override
protected void map(LongWritable key, Text value, Context context)
throws IOException, InterruptedException {
context.write(value, new Text(str));
}
@Override
protected void setup(Context context) throws IOException,
InterruptedException {
str = this.s;//获取到之前在Mapper中加入的常量
}
}
static class JNIReduce extends Reducer<Text, Text, Text, Text> {
//不做任何处理,只是输出Map的结果
@Override
protected void reduce(Text key, Iterable<Text> values, Context context)
throws IOException, InterruptedException {
for(Text text : values) {
context.write(key, text);
}
}
}
@Override
public int run(String[] args) throws Exception {
Configuration conf = getConf();
Job job = new Job(conf, "TestJNIMapreduce");
job.setJarByClass(JNIMapreduce.class);
job.setMapperClass(JNIMap.class);
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(Text.class);
job.setReducerClass(JNIReduce.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(Text.class);
Path input = new Path(args[0]);
Path output = new Path(args[1]);
FileInputFormat.addInputPath(job, input);
FileOutputFormat.setOutputPath(job, output);
job.waitForCompletion(true);
return 0;
}
public static void main(String[] args) throws Exception {
int res = ToolRunner.run(new Configuration(), new JNIMapreduce(), args);
System.exit(res);
}
}
原始文本为:
sdf
zxcv
qwerasdf
qwerqwer
运行Mapreduce程序,输出结果如下:
asdf***************************This is JNITest!!***************************
qwer ***************************This is JNITest!!***************************
qwerqwer ***************************This is JNITest!!***************************
sdf ***************************This is JNITest!!***************************
zxcv ***************************This is JNITest!!***************************
至此在Hadoop中使用JNI调用C++动态库完成。