ES作为强大的搜索引擎,HDFS是分布式文件系统。ES可以将自身的Document导入到HDFS中用作备份,ES也可以将存储在HDFS上的结构化文件导入为ES的中的Document。而ES-Hadoop正是这两者之间的一个connector
1,将数据从ES导出到HDFS
1.1,数据准备,在ES中创建Index和Type,并创建document。在我的例子中,Index是mydata,type是person,创建了两条如下图所示的document
1.2 在项目中引入ES-Hadoop库
org.elasticsearch elasticsearch-hadoop 5.5.2
值得注意的是,上面的dependency只会引入ES-Hadoop相关的Jar包,和Hadoop相关的包,例如hadoop-common, hadoop-hdfs等等,依然还需要添加依赖。
1.3,创建从ES到Hadoop的数据迁移的Mapper类
package com.wjm.es_hadoop.example1; import org.apache.hadoop.io.Text; import org.apache.hadoop.mapreduce.Mapper; import org.elasticsearch.hadoop.mr.LinkedMapWritable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; class E2HMapper01 extends Mapper, LinkedMapWritable, Text, LinkedMapWritable> { private static final Logger LOG = LoggerFactory.getLogger(E2HMapper01.class); @Override protected void setup(Context context) throws IOException, InterruptedException { super.setup(context); } @Override protected void map(Text key, LinkedMapWritable value, Context context) throws IOException, InterruptedException { LOG.info("key {} value {}", key, value); context.write(key, value); } @Override protected void cleanup(Context context) throws IOException, InterruptedException { super.cleanup(context); } }
这个Mapper非常简单,它并没有对从ES获取的数据进行任何的处理,只是写到了context中。map方法中,参数key的值,就是ES中document的id的值,参数value是一个LinkedMapWritable,它包含的就是一个document的内容。只是在这个mapper中,我们没有处理document,而是直接输出。
1.4,创建从ES到Hadoop的数据迁移的Job类
package com.wjm.es_hadoop.example1; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.Path; import org.apache.hadoop.io.Text; import org.apache.hadoop.mapreduce.Job; import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat; import org.elasticsearch.hadoop.mr.EsInputFormat; import org.elasticsearch.hadoop.mr.LinkedMapWritable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class E2HJob01 { private static Logger LOG = LoggerFactory.getLogger(E2HJob01.class); public static void main(String[] args) { try { Configuration conf = new Configuration(); conf.setBoolean("mapred.map.tasks.speculative.execution", false); conf.setBoolean("mapred.reduce.tasks.speculative.execution", false); //ElasticSearch节点 conf.set("es.nodes", "192.168.8.194:9200"); //ElaticSearch Index/Type conf.set("es.resource", "mydata/person/"); if (args.length != 1) { LOG.error("error : " + args.length); System.exit(2); } Job job = Job.getInstance(conf, "JOBE2H01"); job.setJarByClass(E2HJob01.class); job.setInputFormatClass(EsInputFormat.class); job.setMapperClass(E2HMapper01.class); job.setMapOutputKeyClass(Text.class); job.setMapOutputValueClass(LinkedMapWritable.class); FileOutputFormat.setOutputPath(job, new Path(args[0])); System.out.println(job.waitForCompletion(true)); } catch (Exception e) { LOG.error(e.getMessage(), e); } } }
这个Job有两点需要注意一下:
1,它没有reducer,因为就是数据的透传,不需要reduce过程。
2,InputFormatClass被设置为EsInputFormat,正是这个类,负责将从ES读出的数据,转换成mapper的输入参数(Text,LinkedMapWritable)
1.5,打包运行
以下面的命令来启动MapReduce任务:
hadoop jar es-hadoop-1.0-SNAPSHOT.jar com.wjm.es_hadoop.example1.E2HJob01 hdfs://bigdata-191:8020/wangjinming
执行完这个命令之后,看到/wangjinming目录下面产生了文件
查看其中一个文件,会发现数据被分为两列,第一列为id,第二列为document的内容
另外,在运行hadoop jar命令的时候,需要把es-hadoop的jar包放到hadoop jar能访问到的classpath下面。我查了一些方法都没成功,最后使用了一个笨方法,用hadoop classpath方法查看hadoop的classpath有哪些,然后将es-hadoop相关的jar包copy到其中一个目录下。
2,将数据从HDFS中导入到ES中。
2.1,数据准备。创建下面的这样一个文件并put到hdfs文件系统中(我放在hdfs://bigdata-191:8020/input/perosn)
{"id":"3", "name":"jerry", "age":"23", "info":"hello hadoop"}
{"id":"4", "name":"russell", "age":"15", "info":"hello elasticsearch"}
2.2,Mapper编写
package com.wjm.es_hadoop.example1; import org.apache.hadoop.io.LongWritable; import org.apache.hadoop.io.NullWritable; import org.apache.hadoop.io.Text; import org.apache.hadoop.mapreduce.Mapper; import java.io.IOException; class H2EMapper01 extends Mapper, Text, NullWritable, Text> { @Override protected void setup(Context context) throws IOException, InterruptedException { super.setup(context); } @Override public void run(Context context) throws IOException, InterruptedException { super.run(context); } @Override protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException { context.write(NullWritable.get(), value); } @Override protected void cleanup(Context context) throws IOException,InterruptedException { super.cleanup(context); } }
这个Mapper也很简单,只是把从HDFS中读取到的数据透传给ES。因为Mapper的input是一个HDFS文件,所以,mapper的入参跟其他从hdfs多数据的mapper没有任何区别。写入到context的是,入参的key值是没有意义的,所以忽略掉,直接把Text类型的value写入到context就可以了。
2.3,编写job
package com.wjm.es_hadoop.example1; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.Path; import org.apache.hadoop.io.NullWritable; import org.apache.hadoop.io.Text; import org.apache.hadoop.mapreduce.Job; import org.apache.hadoop.mapreduce.lib.input.FileInputFormat; import org.elasticsearch.hadoop.mr.EsOutputFormat; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class H2EJob01 { private static Logger LOG = LoggerFactory.getLogger(H2EJob01.class); public static void main(String args[]) { try { Configuration conf = new Configuration(); conf.setBoolean("mapred.map.tasks.speculative.execution", false); conf.setBoolean("mapred.reduce.tasks.speculative.execution", false); //ElasticSearch节点 conf.set("es.nodes", "192.168.8.194:9200"); //ElaticSearch Index/Type conf.set("es.resource", "mydata/person/"); //Hadoop上的数据格式为JSON,可以直接导入 conf.set("es.input.json", "yes"); conf.set("es.mapping.id", "id"); if (args.length != 1) { LOG.error("error : " + args.length); System.exit(2); } Job job = Job.getInstance(conf, "51JOBH2E"); job.setJarByClass(H2EJob01.class); job.setMapperClass(H2EMapper01.class); job.setMapOutputKeyClass(NullWritable.class); job.setMapOutputValueClass(Text.class); job.setOutputFormatClass(EsOutputFormat.class); FileInputFormat.addInputPath(job, new Path(args[0])); System.out.println(job.waitForCompletion(true)); } catch (Exception e) { LOG.error(e.getMessage(), e); } } }
这个Job有几个需要注意的地方
es.input.json参数设置为true告诉ES-Hadoop,mapper输出的结果是一个json格式的Text。
es.mapping.id参数指定json对象中那种field对应的值为es中document的id
OutputFormatClass被设置为EsOutputFormat,正是这个类负责将MapReduce的输出结果(一个json格式的Text)转换为ES的ID和document的内容
2.4,执行命令
hadoop jar es-hadoop-1.0-SNAPSHOT.jar com.wjm.es_hadoop.example1.H2EJob01 hdfs://bigdata-191:8020/input/person
命令成功执行之后,可以通过ES的命令看到数据已经在ES中创建了相应的document