文章目錄
一. 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()
}
}