ES-Hadoop学习之ES和HDFS数据交换

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





你可能感兴趣的:(大数据,HDFS,ES)