一. SparkRDD 与 HBase的交互
1.1 依赖配置以及注意事项
1.1.1 特别注意
建议参考 2.3 添加数据 - put的使用里面的处理方法
1.1.2 POM 文件
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<encoding>UTF-8</encoding>
<hadoop.version>2.7.5</hadoop.version>
<spark.version>2.3.4</spark.version>
<scala.version>2.11.12</scala.version>
<junit.version>4.12</junit.version>
<netty.version>4.1.42.Final</netty.version>
</properties>
<dependencies>
<!-- spark 核心依赖包 -->
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-core_2.11</artifactId>
<version>${spark.version}</version>
</dependency>
<dependency>
<groupId>org.apache.hbase.connectors.spark</groupId>
<artifactId>hbase-spark</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-streaming_2.11</artifactId>
<version>${spark.version}</version>
<!--
java.lang.NoClassDefFoundError: org/apache/spark/streaming/dstream/DStream
-->
<!-- <scope>provided</scope>-->
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>${netty.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<!-- 编译Scala 的插件 -->
<plugin>
<groupId>net.alchim31.maven</groupId>
<artifactId>scala-maven-plugin</artifactId>
<version>3.4.6</version>
</plugin>
<!-- 编译Java 的插件 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
</configuration>
</plugin>
</plugins>
</build>
1.1.2 注意事项
在下载依赖的时候很有可能出现下载不下来的问题, 会卡到一个地方,一直下载
解决办法 : 尝试删掉这个文件, 或许可以
1.2 获取数据 - Get 的使用
特别注意 : 千万不要忘了导 import org.apache.hadoop.hbase.spark.HBaseRDDFunctions._
package com.wangt.hbase.spark
import org.apache.hadoop.hbase.client.{Get, Result}
import org.apache.hadoop.hbase.spark.HBaseContext
import org.apache.hadoop.hbase.spark.HBaseRDDFunctions._
import org.apache.hadoop.hbase.util.Bytes
import org.apache.hadoop.hbase.{Cell, CellUtil, HBaseConfiguration, TableName}
import org.apache.spark.{SparkConf, SparkContext}
/**
* 使用 RDD 作为数据源, 将RDD中的数据写入到HBase
* 特别注意 : 一定要导入 HBase 的隐式方法org.apache.hadoop.hbase.spark.HBaseRDDFunctions._
*
* @author 王天赐
* @create 2019-11-29 19:35
*/
object HBaseBulkGetExampleByRDD extends App{
// 1.创建SparkConf 以及 SparkContext, 设置本地运行模式
val conf = new SparkConf()
.setMaster("local[*]")
.setAppName("HBase")
val sc = new SparkContext(conf)
// 设置日志输出等级为 WARN
sc.setLogLevel("WARN")
try {
// 2. 创建HBaseConfiguration对象设置连接参数
val hbaseConf = HBaseConfiguration.create()
// 设置连接参数
hbaseConf.set("hbase.zookeeper.quorum", "222.22.91.81")
hbaseConf.set("hbase.zookeeper.property.clientPort", "2181")
// 3.创建HBaseContext
val hc = new HBaseContext(sc, hbaseConf)
// 4. 将需要获取的数据的 Rowkey 字段等信息封装到 RDD中
val rowKeyAndQualifier = sc.parallelize(Array(
Array(Bytes.toBytes("B1001"), Bytes.toBytes("name")),
Array(Bytes.toBytes("B1002"), Bytes.toBytes("name")),
Array(Bytes.toBytes("B1003"), Bytes.toBytes("name"))
))
// 5. 获取指定RowKey 以及指定字段的信息
val result = rowKeyAndQualifier.hbaseBulkGet(hc, TableName.valueOf("Student"), 2,
(info) => {
val rowkey = info(0)
// 字段名
val qualify = info(1)
val get = new Get(rowkey)
get
}
)
// 6. 遍历结果
result.foreach(data => {
// 注意 Data是 Tuple 类型
val result: Result = data._2
// 获取 Cell数组对象
val cells: Array[Cell] = result.rawCells()
// 遍历
for (cell <- cells) {
// 获取对应的值
val rowKey = Bytes.toString(CellUtil.cloneRow(cell))
val qualifier = Bytes.toString(CellUtil.cloneQualifier(cell))
val value = Bytes.toString(CellUtil.cloneValue(cell))
// 打印输出结果
println("[ " + rowKey + " , " + qualifier + " , " + value + " ]")
}
})
} finally {
sc.stop()
}
}
1.3 添加数据 - put 的使用
package com.wangt.hbase.spark
import org.apache.spark.{SparkConf, SparkContext}
import org.apache.hadoop.hbase.client.{Get, Put, Result}
import org.apache.hadoop.hbase.spark.HBaseContext
import org.apache.hadoop.hbase.spark.HBaseRDDFunctions._
import org.apache.hadoop.hbase.util.Bytes
import org.apache.hadoop.hbase.HBaseConfiguration
import org.apache.hadoop.hbase.TableName
import org.apache.spark.SparkConf
import org.apache.spark.SparkContext
/**
* @author 王天赐
* @create 2019-11-29 9:28
*/
object HBaseBulkPutExample extends App {
val tableName = "Student"
val sparkConf = new SparkConf()
.setAppName("HBaseBulkGetExample " + tableName)
.setMaster("local[*]")
val sc = new SparkContext(sparkConf)
try {
//[(Array[Byte])]
val rdd = sc.parallelize(Array(
Array(Bytes.toBytes("B1001"),Bytes.toBytes("name"),Bytes.toBytes("张飞")),
Array(Bytes.toBytes("B1002"),Bytes.toBytes("name"),Bytes.toBytes("李白")),
Array(Bytes.toBytes("B1003"),Bytes.toBytes("name"),Bytes.toBytes("韩信"))))
val conf = HBaseConfiguration.create()
conf.set("hbase.zookeeper.quorum", "222.22.91.81")
conf.set("hbase.zookeeper.property.clientPort", "2181")
val hbaseContext = new HBaseContext(sc, conf)
val getRdd = rdd.hbaseBulkPut(hbaseContext, TableName.valueOf("Student"),
record => {
val put = new Put(record(0))
put.addColumn(Bytes.toBytes("info"), record(1), record(2));
put
}
)
} finally {
sc.stop()
}
}
1.4 删除数据 - delete 的使用
package com.wangt.hbase.spark
import org.apache.hadoop.hbase.client.Delete
import org.apache.hadoop.hbase.{HBaseConfiguration, TableName}
import org.apache.hadoop.hbase.spark.HBaseContext
import org.apache.hadoop.hbase.util.Bytes
import org.apache.spark.{SparkConf, SparkContext}
import org.apache.hadoop.hbase.spark.HBaseRDDFunctions._
/**
* @author 王天赐
* @create 2019-11-29 22:01
*/
object HBaseBulkDeleteExample extends App{
// 1.创建SparkConf 以及 SparkContext, 设置本地运行模式
val conf = new SparkConf()
.setMaster("local[*]")
.setAppName("HBase")
val sc = new SparkContext(conf)
// 设置日志输出等级为 WARN
sc.setLogLevel("WARN")
try {
// 2. 创建HBaseConfiguration对象设置连接参数
val hbaseConf = HBaseConfiguration.create()
// 设置连接参数
hbaseConf.set("hbase.zookeeper.quorum", "222.22.91.81")
hbaseConf.set("hbase.zookeeper.property.clientPort", "2181")
// 3.创建HBaseContext
val hc = new HBaseContext(sc, hbaseConf)
// 4. 将需要删除的数据的 Rowkey 字段等信息封装到 RDD中
val deletedRowkeyAndQualifier = sc.parallelize(Array(
Array(Bytes.toBytes("B1001"), Bytes.toBytes("name"))
))
// 5. 删除数据
deletedRowkeyAndQualifier.hbaseBulkDelete(hc, TableName.valueOf("Student"),
(record) => {
val rowkey = record(0)
val qualifier = record(1)
val delete = new Delete(rowkey)
delete.addColumn(Bytes.toBytes("info"), qualifier)
// 最后需要返回一个 Delete 对象
delete
},
2 // 批处理的大小
)
} finally {
sc.stop()
}
}
1.5 自定义操作 - hbaseMapPartitions 的使用
hbaseMapPartitions 相当于是针对每个RDD的分区的数据进行操作
强烈建议 : hbaseMapPartitions 只作为封装 Get 对象或者 Put 对象不要直接在里面 put数据
Get 数据可以, 但是不要直接在 hbaseMapPartitions 方法里面就直接把数据提交删除,具体参考下面的代码
1.5.1 HBaseMapPartitionPut 操作
package com.wangt.hbase.spark
import java.util
import org.apache.hadoop.hbase.client.Put
import org.apache.hadoop.hbase.{HBaseConfiguration, TableName}
import org.apache.hadoop.hbase.spark.HBaseContext
import org.apache.hadoop.hbase.spark.HBaseRDDFunctions._
import org.apache.hadoop.hbase.util.Bytes
import org.apache.spark.{SparkConf, SparkContext}
import scala.collection.JavaConverters._
/**
* @author 王天赐
* @create 2019-11-29 22:10
*/
object HBaseMapPartitionPutExample extends App {
// 1.创建SparkConf 以及 SparkContext, 设置本地运行模式
val conf = new SparkConf()
.setMaster("local[*]")
.setAppName("HBase")
val sc = new SparkContext(conf)
// 设置日志输出等级为 WARN
sc.setLogLevel("WARN")
try {
// 2. 创建HBaseConfiguration对象设置连接参数
val hbaseConf = HBaseConfiguration.create()
// 设置连接参数
hbaseConf.set("hbase.zookeeper.quorum", "222.22.91.81")
hbaseConf.set("hbase.zookeeper.property.clientPort", "2181")
// 3.创建HBaseContext
val hc = new HBaseContext(sc, hbaseConf)
// 4. 将 Rowkey 字段等信息封装到 RDD中
val rdd = sc.parallelize(Array(
Array(Bytes.toBytes("B1004"), Bytes.toBytes("name"),Bytes.toBytes("貂蝉"))
))
// 5.使用 HBaseMapPartition
val putsRdd = rdd.hbaseMapPartitions[Put](hc, (rddData, connection) => {
val puts = rddData.map(r => {
// 取出对应的数据
val rowkey = r(0)
val qualifier = r(1)
val value = r(2)
// 注意 : 这个时候 我们有 HBase的Connection对象. 有 Table 对象, 我们可以做关于HBase的任何事
// 包括但不限于 创建表 或者删除 只需要获取一个Admin对象即可 等等, 下面只是举一个例子
val put = new Put(rowkey)
// 将数据添加进去
put.addColumn(Bytes.toBytes("info"), qualifier, value)
put
})
// 最后再把 puts 返回即可, 它会自动把数据添加进RDD中
puts
})
// 强烈建议 : ! 在 hbaseMapPartitions 方法中将RDD的数据封装成 put类型
// 然后 在 hbaseBulkPut 去添加, 直接在 hbaseMapPartitions 添加, 虽然有 Connection对象, 但是真的不好用,
// 参考 我在 HBaseMapPartitionGetExample 类里面写的, 可以直接分开
// hbaseMapPartitions 封装get, hbaseBulkGet 获取数据, 然后 RDD 遍历
putsRdd.hbaseBulkPut(hc, TableName.valueOf("Student"), (put) => (put))
}finally {
sc.stop()
}
}
1.5.2 HBaseMapPartitionGet 操作
注意 : HBaseMapPartition 和上面的方法的区别是, 就像map和 mapParatition的区别
它会针对每个分区统一执行一次map方法, 而不是针对每一条数据执行一次 推荐使用
package com.wangt.hbase.spark
import java.util
import org.apache.hadoop.hbase.{Cell, CellUtil, HBaseConfiguration, TableName}
import org.apache.hadoop.hbase.client.{Get, Put, Result}
import org.apache.hadoop.hbase.spark.HBaseContext
import org.apache.hadoop.hbase.spark.HBaseRDDFunctions._
import org.apache.hadoop.hbase.util.Bytes
import org.apache.spark.{SparkConf, SparkContext}
/**
* @author 王天赐
* @create 2019-11-30 10:14
*/
object HBaseMapPartitionGetExample extends App{
// 1.创建SparkConf 以及 SparkContext, 设置本地运行模式
val conf = new SparkConf()
.setMaster("local[*]")
.setAppName("HBase")
val sc = new SparkContext(conf)
// 设置日志输出等级为 WARN
sc.setLogLevel("WARN")
try {
// 2. 创建HBaseConfiguration对象设置连接参数
val hbaseConf = HBaseConfiguration.create()
// 设置连接参数
hbaseConf.set("hbase.zookeeper.quorum", "222.22.91.81")
hbaseConf.set("hbase.zookeeper.property.clientPort", "2181")
// 3.创建HBaseContext
val hc = new HBaseContext(sc, hbaseConf)
// 4. 将 Rowkey 字段等信息封装到 RDD中
val rdd = sc.parallelize(Array(
Array(Bytes.toBytes("B1002"), Bytes.toBytes("name")),
Array(Bytes.toBytes("B1003"), Bytes.toBytes("name"))
))
// 5.使用 HBaseMapPartition
val results = rdd.hbaseMapPartitions[String](hc, (rddData, connection) => {
// (1). 获取Table对象
val table = connection.getTable(TableName.valueOf("Student"))
// (2). 注意 rddData 是 iterator 类型
// 可以使用下面的方式获取数据, 但是不推荐
/*
while(rddData.hasNext){
val info = rddData.next()
}
*/
// 官方的例子. 注意 : 这个map 不是RDD 算子, 而是scala自带的函数
// 最后返回的是 一个 iterator 类型 比如下面的例子是 返回的 iterator[Put]
val infos = rddData.map(r => {
/**
* 获取指定 RowKey 和指定字段的值
*/
// 取出对应的数据
val rowKey = r(0)
val qualifier = r(1)
// 创建 Get 对象, 并添加相应的对象以及rowkey信息
val get = new Get(rowKey)
get.addColumn(Bytes.toBytes("info"), qualifier)
// 获取Result对象
val result : Result = table.get(get)
// 获取Cells ,类型是我加上为了 知道这个数据是什么类型的, 可以选择不加
val cells : util.Iterator[Cell] = result.listCells().iterator()
// 遍历 Cells
val sb = new StringBuilder()
while (cells.hasNext){
val cell = cells.next()
// 获取cell中的数据
val rowKey = Bytes.toString(CellUtil.cloneRow(cell))
val qualifier = Bytes.toString(CellUtil.cloneQualifier(cell))
val value = Bytes.toString(CellUtil.cloneValue(cell))
sb.append("[ " + rowKey + " , " + qualifier + " , " + value + " ]" )
}
// 将得到信息返回
sb.toString()
}
)
// 最后再把 infos 返回即可 ,它会把info中的信息封装到 RDD中
infos
})
// 6.遍历结果
results.foreach(println(_))
}finally {
sc.stop()
}
}
二. SparkDStream 与 HBase的交互
2.1 依赖配置以及注意事项
Get 时如果你传入的rowKey是空的话, 后面获取Result的时候会报空指针, 解决方法参考我的代码
2.2 获取数据 - Get的使用
注意事项 : 我用的是netcat作为数据源, 需要先开netcat 再启动程序
参考 :
开启netcat => nc -lk cm5 8989 (端口和主机改成你自己的)
输入数据 :
B1001 name
B1002 name
…
注意 : 第一个是rowKey , 第二个是 字段 (建议输入你HBase中有的RowKey和字段)
package com.wangt.hbase.sparkstreaming
import org.apache.hadoop.hbase.{CellUtil, HBaseConfiguration, TableName}
import org.apache.hadoop.hbase.client.Get
import org.apache.hadoop.hbase.spark.HBaseContext
import org.apache.hadoop.hbase.spark.HBaseDStreamFunctions._
import org.apache.hadoop.hbase.util.Bytes
import org.apache.spark.streaming.{Seconds, StreamingContext}
import org.apache.spark.{SparkConf, SparkContext}
/**
* @author 王天赐
* @create 2019-11-30 20:36
*/
object HBaseBulkGetDStreamExample extends App {
// 1.创建SparkConf 以及 SparkContext, 设置本地运行模式
val sc = new SparkContext("local[*]", "HBase")
val ssc = new StreamingContext(sc, Seconds(5))
// 设置日志输出等级为 WARN
sc.setLogLevel("WARN")
try {
// 3.创建StreamingContext 设置将5s内的数据一块处理
// 2.获取 HBase 配置对象以及HBaseContext
val hBaseConf = HBaseConfiguration.create()
hBaseConf.set("hbase.zookeeper.quorum", "222.22.91.81")
hBaseConf.set("hbase.zookeeper.property.clientPort", "2181")
val hBaseContext = new HBaseContext(sc, hBaseConf)
// 4.获取指定端口的数据
val dStream = ssc.socketTextStream("cm5", 8989)
dStream.print()
// 将数据封装到Get对象, 然后将数据转换为 DStream
val getsDStream = dStream.hbaseMapPartitions[Get](hBaseContext, (record, connection) => {
val gets: Iterator[Get] = record.map(r => {
// 读取的单条数据 : B1001 name
val arr: Array[String] = r.split(" ")
// 默认值
var rowKey: Array[Byte] = Bytes.toBytes("-")
var qualifier: Array[Byte] = Bytes.toBytes("0")
// 这里其实应该过滤下, 过滤掉不符合的数据, 但是这里只是作为Demo, 假设数据格式是规范的...
if (arr.length == 2) {
rowKey = Bytes.toBytes(arr(0))
qualifier = Bytes.toBytes(arr(1))
}
// 将数据封装成 get 对象
val get = new Get(rowKey)
get.addColumn(Bytes.toBytes("info"), qualifier)
// 注意需要将get对象返回
get
})
// 最后将gets 返回
gets
})
// 根据 Get 获取对应的Result 以及结果
val data = getsDStream.hbaseBulkGet(hBaseContext, TableName.valueOf("Student"),
2, (get) => (get), result => {
// 特别注意 : 这个判断的必须加上, 否则, 一旦你输入的rowkey是错误的, 获取不到数据
// 会立马报错 ,程序就会停止 最好使用 rawCells().size , 其他的属性我都试过., 没有用, 比如 getExist的...
if (result.rawCells().size > 0 ) {
// 获取 Cells
// 下面这种方法也是可以的
//val cells = result.rawCells()
val cells = result.listCells().iterator()
var sb: StringBuilder = null
while (cells.hasNext) {
var cell = cells.next()
// 获取指定的数据
val rowKey = Bytes.toString(CellUtil.cloneRow(cell))
val qualifier = Bytes.toString(CellUtil.cloneQualifier(cell))
val value = Bytes.toString(CellUtil.cloneValue(cell))
// 使用StringBuilder拼接数据
sb = new StringBuilder()
sb.append("[ " + rowKey + " , " + qualifier + " , " + value + " ]")
}
sb.toString()
}
})
// 打印结果
data.print()
ssc.start()
ssc.awaitTermination()
ssc.stop()
}
}
2.3 添加数据 - Put的使用
注意事项 : put中我增加了过滤数据的步骤, 建议在使用时都增加过滤数据, 否则如果是不规则的数据容易报错
推荐 :
(1) 尽量先对数据进行过滤, 拿到你想要格式的数据
(2) 使用 hbaseMapPartitions 对数据进行封装成 Get / Put 对象
(3) 使用 hbaseBulkGet / hbaseBulkPut 对数据进行处理
package com.wangt.hbase.sparkstreaming
import org.apache.hadoop.conf.Configuration
import org.apache.hadoop.hbase.{HBaseConfiguration, TableName}
import org.apache.hadoop.hbase.client.Put
import org.apache.hadoop.hbase.spark.HBaseContext
import org.apache.hadoop.hbase.spark.HBaseDStreamFunctions._
import org.apache.hadoop.hbase.util.Bytes
import org.apache.spark.SparkContext
import org.apache.spark.streaming.dstream.{DStream, ReceiverInputDStream}
import org.apache.spark.streaming.{Seconds, StreamingContext}
/**
* @author 王天赐
* @create 2019-12-01 9:15
*/
object HBaseBulkPutDStreamExample extends App {
// 1.创建SparkContext 以及StreamingContext 设置本地运行模式以及数据间隔时间
// (1) 补充: 如果创建了SparkContext, 那么在创建StreamingContext的时候直接将 sc 作为参数传入即可
val sc = new SparkContext("local[*]", "HBase-Spark")
sc.setLogLevel("WARN")
val ssc = new StreamingContext(sc, Seconds(2))
try {
// 2.创建HBaseConfiguration对象以及HBaseContext对象
val hBaseConf: Configuration = HBaseConfiguration.create()
hBaseConf.set("hbase.zookeeper.quorum", "222.22.91.81:2181")
val hBaseContext = new HBaseContext(sc, hBaseConf)
// 3.获取DStream流
val dStream: ReceiverInputDStream[String] = ssc.socketTextStream("cm5", 8989)
// 4.对不符合数据规则的数据进行过滤
// 规则 : 要求 => B1001 name 张飞 (数据切分后长度为3)
val filterDStream: DStream[String] = dStream.filter(line => {
val data = line.split(" ")
if (data.length == 3) {
true
} else {
false
}
})
// 5.将数据封装成 Put对象
val putsDStream: DStream[Put] = filterDStream.hbaseMapPartitions(hBaseContext, (record, connection) => {
val puts = record.map(r => {
//(1) 切分数据
val data = r.split(" ")
//(2) 获取对应的数据
val rowKey = Bytes.toBytes(data(0))
val qualifier = Bytes.toBytes(data(1))
val value = Bytes.toBytes(data(2))
//(3) 封装数据
val put = new Put(rowKey)
put.addColumn(Bytes.toBytes("info"), qualifier, value)
put
})
// 返回Puts
puts
})
// 5.将put 对象中的数据写入到 HBase
putsDStream.hbaseBulkPut(hBaseContext, TableName.valueOf("Student"), (put) => (put))
// 6.开启StreamingContext
ssc.start()
ssc.awaitTermination()
ssc.stop()
}finally {
sc.stop()
}
}
= filterDStream.hbaseMapPartitions(hBaseContext, (record, connection) => {
val puts = record.map(r => {
//(1) 切分数据
val data = r.split(" ")
//(2) 获取对应的数据
val rowKey = Bytes.toBytes(data(0))
val qualifier = Bytes.toBytes(data(1))
val value = Bytes.toBytes(data(2))
//(3) 封装数据
val put = new Put(rowKey)
put.addColumn(Bytes.toBytes(“info”), qualifier, value)
put
})
// 返回Puts
puts
})
// 5.将put 对象中的数据写入到 HBase
putsDStream.hbaseBulkPut(hBaseContext, TableName.valueOf(“Student”), (put) => (put))
// 6.开启StreamingContext
ssc.start()
ssc.awaitTermination()
ssc.stop()
}finally {
sc.stop()
}
}