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 包的时候就会将所需依赖一并打入