1.文件數據源
監控目錄下新生成的文件,將文件內容讀成一個RDD,這個文件最好是mv進來的。
import org.apache.spark.streaming._
val ssc = new StreamingContext(sc,Seconds(5))
val lineDStream = ssc.textFileStream("hdfs://master:9000/data")
val words = lineDStream.flatMap(_.split(" "))
val word2count = words.map((_,1))
val result = word2count.reduceByKey(_+_)
result.print
ssc.start
2.RDD隊列
監控一個隊列,實時獲取隊列中的新增的RDD。
package com.dendan.queueRDD
import org.apache.spark.SparkConf
import org.apache.spark.rdd.RDD
import org.apache.spark.streaming.{Seconds, StreamingContext}
import scala.collection.mutable
object QueueRDD {
def main(args: Array[String]): Unit = {
val conf = new SparkConf().setMaster("local[*]").setAppName("QueueRDD")
val ssc = new StreamingContext(conf, Seconds(1))
//創建RDD隊列
val rddQueue = new mutable.SynchronizedQueue[RDD[Int]]()
//創建QueueInputDStream
val inputStream = ssc.queueStream(rddQueue)
//處理隊列中的RDD數據
val mappedStream = inputStream.map(x => (x % 10, 1))
val reducedStream = mappedStream.reduceByKey(_ + _)
//打印結果
reducedStream.print()
//自動計算
ssc.start()
for (i <- 1 to 30) {
rddQueue += ssc.sparkContext.makeRDD(1 to 300, 10)
Thread.sleep(2000)
//通過程序停止StreamingContext的運行
// ssc.stop()
}
ssc.awaitTermination()
}
}
3.自定義Receiver
繼承Receiver抽象類,實現onStart和onStop方法。
使用:通過ssc.receiverStream傳入一個自定義的Receiver實例。
package com.dendan
import java.io.{BufferedReader, InputStreamReader}
import java.net.Socket
import java.nio.charset.StandardCharsets
import org.apache.spark.SparkConf
import org.apache.spark.storage.StorageLevel
import org.apache.spark.streaming.{Seconds, StreamingContext}
import org.apache.spark.streaming.receiver.Receiver
/**
* 自定義Receiver
*
* @param host
* @param port
*/
class CustomReceiver(host: String, port: Int) extends Receiver[String](StorageLevel.MEMORY_ONLY) {
/**
* 程序啓動的時候調用
*/
override def onStart(): Unit = {
val socket = new Socket(host, port)
var inputText = ""
val reader = new BufferedReader(new InputStreamReader(socket.getInputStream(), StandardCharsets.UTF_8))
inputText = reader.readLine()
while (!isStopped() && inputText != null) {
//如果接收到了數據就保存
store(inputText)
inputText = reader.readLine()
}
//重新連接,重新執行onStart()方法
restart("")
}
/**
* 程序停止的時候調用
*/
override def onStop(): Unit = {}
}
object CustomReceiver {
def main(args: Array[String]): Unit = {
//創建配置
val sparkConf = new SparkConf().setAppName("streaming").setMaster("local[*]")
//創建streamingContext
val ssc = new StreamingContext(sparkConf, Seconds(5))
// 業務邏輯:從socket接受數據
val lineDStream = ssc.receiverStream(new CustomReceiver("master", 9999))
val wordStream = lineDStream.flatMap(_.split(" "))
val word2CountDStream = wordStream.map((_, 1))
val resultDStream = word2CountDStream.reduceByKey(_ + _)
resultDStream.print()
//啓動ssc
ssc.start()
ssc.awaitTermination()
}
}
4.對接Kafka
連接kafka的兩種方式:
- 通過kafka的
高級API
來獲取數據,這種情況是spark比較老的版本支持的情況,receiver運行再某一個executor中,性能比較低,會存在丟失數據的風險,spark爲了保存數據處理,提供了WAL(預寫日誌)的方式來保證。 - 通過kafka的
低級API
來獲取數據,這種情況是spark比較新的版本提供的,性能比較高,receiver運行在Driver端,由於使用了低級API,需要業務人員手動維護offset,需要保證業務代碼的事務性。
連接池技術:
主要通過apache提供的commons-tools來實現在sparkstreaming寫入到kafka的時候,從連接池中獲取kafka連接。
連接的時候使用如下jar包:
<!-- streaming 與 kafka 連接-->
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-streaming-kafka_2.11</artifactId>
<version>1.6.3</version>
</dependency>
<!-- kafka連接池技術 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.5.0</version>
</dependency>