Spark學習筆記(5) - Spark連接HBase進行交互 - Get / Put 數據

一. 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 的使用

  1. hbaseMapPartitions 相當於是針對每個RDD的分區的數據進行操作

  2. 強烈建議 : hbaseMapPartitions 只作爲封裝 Get 對象或者 Put 對象不要直接在裏面 put數據

  3. 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 再啓動程序

參考 :

  1. 開啓netcat => nc -lk cm5 8989 (端口和主機改成你自己的)

  2. 輸入數據 :

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的使用

  1. 注意事項 : put中我增加了過濾數據的步驟, 建議在使用時都增加過濾數據, 否則如果是不規則的數據容易報錯

  2. 推薦 :

(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()
}

}




發佈了120 篇原創文章 · 獲贊 45 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章