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>