MapReduce 操作 HBase、解决依赖缺失的问题

MapReduce on HBase

流程图☆

不能直接穿到底层去读取 HFile ,因为有一部分数据在 MemStore 中,所以要去和 RegionServer 建立连接,获取数据
一个 region 会生成一个切片,即对应一个 Map 任务,本质上是通过 Scan 获取数据
TableInputFormat -- region数据在进入Map的时候的数据格式化类
TableOutputFormat -- 数据由Reduce写入HBase时的数据格式化类

MapReduce 读取 HBase 代码示例

package com.shujia;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.client.Result;
import org.apache.hadoop.hbase.client.Scan;
import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
import org.apache.hadoop.hbase.mapreduce.TableMapReduceUtil;
import org.apache.hadoop.hbase.mapreduce.TableMapper;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.hadoop.io.IntWritable;
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.output.FileOutputFormat;

import java.io.IOException;

// 读取stu表,统计班级人数
public class Demo05MRReadHBase {
    // Map
    // 自定义静态类 继承 TableMapper 并指定由Map端输出的 keyOut valueOut 即可
    // 继承 TableMapper 是为了去读 HBase 的数据
    public static class MRReadHBase extends TableMapper<Text, IntWritable> {
        @Override
        protected void map(ImmutableBytesWritable key, Result value, Mapper<ImmutableBytesWritable, Result, Text, IntWritable>.Context context) throws IOException, InterruptedException {
            String rowkey = Bytes.toString(key.get());

            String clazz = Bytes.toString(value.getValue("info".getBytes(), "clazz".getBytes()));

            // 以班级作为KeyOut,1 作为ValueOut
            context.write(new Text(clazz), new IntWritable(1));
        }
    }

    // Reduce
    public static class MyReducer extends Reducer<Text, IntWritable, Text, IntWritable> {
        @Override
        protected void reduce(Text key, Iterable<IntWritable> values, Reducer<Text, IntWritable, Text, IntWritable>.Context context) throws IOException, InterruptedException {
            int cnt = 0;
            for (IntWritable value : values) {
                cnt += value.get();
            }
            context.write(key, new IntWritable(cnt));
        }
    }

    // Driver
    public static void main(String[] args) throws IOException, InterruptedException, ClassNotFoundException {
        Configuration conf = HBaseConfiguration.create();
        conf.set("hbase.zookeeper.quorum", "master:2181,node1:2181,node2:2181");
        conf.set("fs.defaultFS", "hdfs://master:9000");

        Job job = Job.getInstance(conf);

        job.setJobName("Demo05MRReadHBase");
        job.setJarByClass(Demo05MRReadHBase.class);

        // 配置Map任务
        TableMapReduceUtil.initTableMapperJob(
                "stu",
                new Scan(),
                MRReadHBase.class,
                Text.class,
                IntWritable.class,
                job
        );

        // 配置Reduce任务
        job.setReducerClass(MyReducer.class);
        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(IntWritable.class);

        // 配置输入输出路径
        Path path = new Path("/MR/HBase/output/");
        FileSystem fs = FileSystem.get(conf);

        if (fs.exists(path)) {
            fs.delete(path, true);
        }

        FileOutputFormat.setOutputPath(job, path);

        job.waitForCompletion(true);
        /**
         * 配置Hadoop运行时的依赖环境
         * export HADOOP_CLASSPATH="$HBASE_HOME/lib/*"
         * 提交任务
         * hadoop jar HBase-1.0.jar com.shujia.Demo05MRReadHBase
         */
    }
}

代码完成之后 将之打成 jar 包 并上传至Linux
然后通过hadoop jar HBase-1.0.jar com.shujia.Demo05MRReadHBase命令 提交任务
因为 Hadoop 中并没有处理 HBase 数据的相关依赖,所以任务会报错
因为缺少处理 HBase 数据的相关依赖,所以解决这个问题有两种方法(思路)
1、在 HBase 中找相关依赖
2、在打 jar 包的时候将依赖一起打进去

缺少相关依赖的处理方法1

在 HBase 中找相关依赖,并配置Hadoop运行时的依赖环境

# 第一步 在 Linux 中切到 HBase 的目录下 的 lib 目录下
# lib 目录下存放的都是 jar 包(依赖)

cd /usr/local/soft/hbase-1.4.6/lib/

# 第二步 通过 HADOOP_CLASSPATH 指定 Hadoop 运行时的依赖环境

export HADOOP_CLASSPATH="$HBASE_HOME/lib/*"
# 注 这样的配置是临时的,只在当前会话生效

MapReduce 读取 HBase 数据 处理数据并保存至 HBase 代码示例

package com.shujia;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.client.Mutation;
import org.apache.hadoop.hbase.client.Put;
import org.apache.hadoop.hbase.client.Result;
import org.apache.hadoop.hbase.client.Scan;
import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
import org.apache.hadoop.hbase.mapreduce.TableMapReduceUtil;
import org.apache.hadoop.hbase.mapreduce.TableMapper;
import org.apache.hadoop.hbase.mapreduce.TableReducer;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.NullWritable;
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.output.FileOutputFormat;

import java.io.IOException;

// 读取stu表,统计性别人数,并将结果写回HBase的 stu_gender_cnt
public class Demo06MRReadAndWriteHBase {
    // Map
    public static class MRReadHBase extends TableMapper<Text, IntWritable> {
        @Override
        protected void map(ImmutableBytesWritable key, Result value, Mapper<ImmutableBytesWritable, Result, Text, IntWritable>.Context context) throws IOException, InterruptedException {
            String rowkey = Bytes.toString(key.get());

            String gender = Bytes.toString(value.getValue("info".getBytes(), "gender".getBytes()));

            // 以班级作为KeyOut,1 作为ValueOut
            context.write(new Text(gender), new IntWritable(1));
        }
    }

    // Reduce
    // 自定义静态类 继承 TableReducer 并指定 keyin valuein keyout
    // 为什么没有 valueout 呢?因为 valueout 被写死了必须是 [Put|Delete] 对象 <--> 即 Mutation 对象
    // 因为 Put|Delete 继承自 Mutation ,而且 keyout 会被 TableOutputFormat 忽略 ,所以 keyout 指定啥都可以
    // 继承 TableReducer 是为了去将数据保存至 HBase 
    public static class MRWriteHBase extends TableReducer<Text, IntWritable, NullWritable> {
        @Override
        protected void reduce(Text key, Iterable<IntWritable> values, Reducer<Text, IntWritable, NullWritable, Mutation>.Context context) throws IOException, InterruptedException {
            int cnt = 0;
            for (IntWritable value : values) {
                cnt += value.get();
            }

            Put put = new Put(key.getBytes());
            // (cnt + "").getBytes() -- int --> byte
            // 还有一种方式,通过工具 Bytes.toBytes(int val)
            put.addColumn("info".getBytes(), "cnt".getBytes(), (cnt + "").getBytes());

            context.write(NullWritable.get(), put);
        }
    }

    // Driver
    public static void main(String[] args) throws IOException, InterruptedException, ClassNotFoundException {
        Configuration conf = HBaseConfiguration.create();
        conf.set("hbase.zookeeper.quorum", "master:2181,node1:2181,node2:2181");
        conf.set("fs.defaultFS", "hdfs://master:9000");

        Job job = Job.getInstance(conf);

        job.setJobName("Demo06MRReadAndWriteHBase");
        job.setJarByClass(Demo06MRReadAndWriteHBase.class);

        // 配置Map任务
        TableMapReduceUtil.initTableMapperJob(
                "stu",
                new Scan(),
                MRReadHBase.class,
                Text.class,
                IntWritable.class,
                job
        );

        // 配置Reduce任务
        TableMapReduceUtil.initTableReducerJob(
                "stu_gender_cnt",
                MRWriteHBase.class,
                job
        );

        job.waitForCompletion(true);

        /**
         * 先在 HBase 中创建stu_gender_cnt表
         * create 'stu_gender_cnt','info'
         *
         * 使用 Maven 插件将依赖打入Jar包中,并上传至 Linux
         *
         * 提交任务
         * hadoop jar HBase-1.0-jar-with-dependencies.jar com.shujia.Demo06MRReadAndWriteHBase
         */
    }
}

缺少相关依赖的处理方法2

在打 jar 包的时候将依赖一起打进去

# 使用Maven插件将依赖打入Jar包中
# 在项目的 pom.xml 文件中添加 Maven 插件的依赖 然后重新导一下依赖

    <!--     将依赖打入Jar包-->
    <build>
        <plugins>

            <!-- Java Compiler -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>


            <!-- 带依赖jar 插件-->
            <plugin>
                <artifactId>maven-assembly-plugin</artifactId>
                <configuration>
                    <descriptorRefs>
                        <descriptorRef>jar-with-dependencies</descriptorRef>
                    </descriptorRefs>
                </configuration>
                <executions>
                    <execution>
                        <id>make-assembly</id>
                        <phase>package</phase>
                        <goals>
                            <goal>single</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>

        </plugins>
    </build>

# 之后打 jar 包的时候就会将所需依赖一并打入
posted @ 2022-03-01 20:27  赤兔胭脂小吕布  阅读(496)  评论(0编辑  收藏  举报