Spark Streaming原生支持一些不同的数据源。一些“核心”数据源已经被打包到Spark Streaming 的 Maven 工件中,而其他的一些则可以通过 spark-streaming-kafka 等附加工件获取。每个接收器都以 Spark 执行器程序中一个长期运行的任务的形式运行,因此会占据分配给应用的 CPU 核心。此外,我们还需要有可用的 CPU 核心来处理数据。这意味着如果要运行多个接收器,就必须至少有和接收器数目相同的核心数,还要加上用来完成计算所需要的核心数。例如,如果我们想要在流计算应用中运行 10 个接收器,那么至少需要为应用分配 11 个 CPU 核心。所以如果在本地模式运行,不要使用local[1]。
1.文件数据源
(1)用法及说明
文件数据流:能够读取所有HDFS API兼容的文件系统文件,通过fileStream方法进行读取,Spark Streaming 将会监控 dataDirectory 目录并不断处理移动进来的文件,记住目前不支持嵌套目录。
streamingContext.textFileStream(dataDirectory)
注意事项:
文件需要有相同的数据格式; |
文件进入 dataDirectory的方式需要通过移动或者重命名来实现; |
一旦文件移动进目录,则不能再修改,即便修改了也不会读取新数据 |
2.案例学习
(1)在HDFS上建好目录
[root@hdp-1 spark]$ hadoop fs -mkdir /fileStream
(2)在/opt/module/data创建三个文件
[root@hdp-1]$ touch a.tsv
[root@hdp-1]$ touch b.tsv
[root@hdp-1]$ touch c.tsv
添加如下数据:
Hello zpark
Hello spark
(3)编写代码
import org.apache.spark.SparkConf
import org.apache.spark.streaming.{Seconds, StreamingContext}
import org.apache.spark.streaming.dstream.DStream
object FileStream {
def main(args: Array[String]): Unit = {
//1.初始化Spark配置信息
Val sparkConf = new SparkConf().setMaster("local[*]").setAppName("StreamWordCount")
//2.初始化SparkStreamingContext
val ssc = new StreamingContext(sparkConf, Seconds(5))
//3.监控文件夹创建DStream
val dirStream = ssc.textFileStream("hdfs://hdp-1:9000/fileStream")
//4.将每一行数据做切分,形成一个个单词
val wordStreams = dirStream.flatMap(_.split("\t"))
//5.将单词映射成元组(word,1)
val wordAndOneStreams = wordStreams.map((_, 1))
//6.将相同的单词次数做统计
val wordAndCountStreams] = wordAndOneStreams.reduceByKey(_ + _)
//7.打印
wordAndCountStreams.print()
//8.启动SparkStreamingContext
ssc.start()
ssc.awaitTermination()
}
}
(4)启动程序并向fileStream目录上传文件
[root@hdp-1]$ hadoop fs -put ./a.tsv /fileStream
[root@hdp-1]$ hadoop fs -put ./b.tsv /fileStream
[root@hdp-1]$ hadoop fs -put ./c.tsv /fileStream
(5)获取计算结果
-------------------------------------------
Time: 1539073810000 ms
-------------------------------------------
-------------------------------------------
Time: 1539073815000 ms
-------------------------------------------
(Hello,4)
(spark,2)
(atguigu,2)
-------------------------------------------
Time: 1539073820000 ms
-------------------------------------------
(Hello,2)
(spark,1)
(zpark,1)
-------------------------------------------
Time: 1539073825000 ms
-------------------------------------------
3.RDD队列
3.1 用法及说明
测试过程中,可以通过使用ssc.queueStream(queueOfRDDs)来创建DStream,每一个推送到这个队列中的RDD,都会作为一个DStream处理。
3.2 案例学习
(1)需求:循环创建几个RDD,将RDD放入队列。通过SparkStream创建Dstream,计算WordCount
(2)编写代码
import org.apache.spark.SparkConf
import org.apache.spark.rdd.RDD
import org.apache.spark.streaming.dstream.{DStream, InputDStream}
import org.apache.spark.streaming.{Seconds, StreamingContext}
import scala.collection.mutable
object RDDStream {
def main(args: Array[String]) {
//1.初始化Spark配置信息
val conf = new SparkConf().setMaster("local[*]").setAppName("RDDStream")
//2.初始化SparkStreamingContext
val ssc = new StreamingContext(conf, Seconds(4))
//3.创建RDD队列
val rddQueue = new mutable.Queue[RDD[Int]]()
//4.创建QueueInputDStream
val inputStream = ssc.queueStream(rddQueue,oneAtATime = false)
//5.处理队列中的RDD数据
val mappedStream = inputStream.map((_,1))
val reducedStream = mappedStream.reduceByKey(_ + _)
//6.打印结果
reducedStream.print()
//7.启动任务
ssc.start()
//8.循环创建并向RDD队列中放入RDD
for (i <- 1 to 5) {
rddQueue += ssc.sparkContext.makeRDD(1 to 300, 10)
Thread.sleep(2000)
}
ssc.awaitTermination()
}
}
(3)结果展示
-------------------------------------------
Time: 1539075280000 ms
-------------------------------------------
(4,60)(0,60)(6,60)(8,60)(2,60)(1,60)(3,60)(7,60)(9,60)(5,60)
-------------------------------------------
Time: 1539075284000 ms
-------------------------------------------
(4,60)(0,60)(6,60)(8,60)(2,60)(1,60)(3,60)(7,60)(9,60)(5,60)
-------------------------------------------
Time: 1539075288000 ms
-------------------------------------------
(4,30)(0,30)(6,30)(8,30)(2,30)(1,30)(3,30)(7,30)(9,30)(5,30)
-------------------------------------------
Time: 1539075292000 ms
-------------------------------------------
4.自定义数据源
4.1 用法及说明
需要继承Receiver,并实现onStart、onStop方法来自定义数据源采集。
4.2 案例实操
(1)需求:自定义数据源,实现监控某个端口号,获取该端口号内容。
(2)代码实现
import java.io.{BufferedReader, InputStreamReader}
import java.net.Socket
import java.nio.charset.StandardCharsets
import org.apache.spark.storage.StorageLevel
import org.apache.spark.streaming.receiver.Receiver
class CustomerReceiver(host: String, port: Int) extends Receiver[String](StorageLevel.MEMORY_ONLY) {
//最初启动的时候,调用该方法,作用为:读数据并将数据发送给Spark
override def onStart(): Unit = {
new Thread("Socket Receiver") {
override def run() {
receive()
}
}.start()
}
//读数据并将数据发送给Spark
def receive(): Unit = {
//创建一个Socket
var socket: Socket = new Socket(host, port)
//定义一个变量,用来接收端口传过来的数据
var input: String = null
//创建一个BufferedReader用于读取端口传来的数据
val reader = new BufferedReader(new InputStreamReader(socket.getInputStream, StandardCharsets.UTF_8))
//读取数据
input = reader.readLine()
//当receiver没有关闭并且输入数据不为空,则循环发送数据给Spark
while (!isStopped() && input != null) {
store(input)
input = reader.readLine()
}
//跳出循环则关闭资源
reader.close()
socket.close()
//重启任务
restart("restart")
}
override def onStop(): Unit = {}
}
(3)使用自定义的数据源采集数据
import org.apache.spark.SparkConf
import org.apache.spark.streaming.{Seconds, StreamingContext}
import org.apache.spark.streaming.dstream.DStream
object FileStream {
def main(args: Array[String]): Unit = {
//1.初始化Spark配置信息
val sparkConf = new SparkConf().setMaster("local[*]").setAppName("StreamWordCount")
//2.初始化SparkStreamingContext
val ssc = new StreamingContext(sparkConf, Seconds(5))
//3.创建自定义receiver的Streaming
val lineStream = ssc.receiverStream(new CustomerReceiver("hadoop102", 9999))
//4.将每一行数据做切分,形成一个个单词
val wordStreams = lineStream.flatMap(_.split("\t"))
//5.将单词映射成元组(word,1)
val wordAndOneStreams = wordStreams.map((_, 1))
//6.将相同的单词次数做统计
val wordAndCountStreams] = wordAndOneStreams.reduceByKey(_ + _)
//7.打印
wordAndCountStreams.print()
//8.启动SparkStreamingContext
ssc.start()
ssc.awaitTermination()
}
}
5 Kafka数据源(重点)
5.1 用法及说明
在工程中需要引入 Maven 工件 spark- streaming-kafka_2.10 来使用它。包内提供的 KafkaUtils 对象可以在 StreamingContext 和 JavaStreamingContext 中以你的 Kafka 消息创建出 DStream。由于 KafkaUtils 可以订阅多个主题,因此它创建出的 DStream 由成对的主题和消息组成。要创建出一个流数据,需要使用 StreamingContext 实例、一个由逗号隔开的 ZooKeeper 主机列表字符串、消费者组的名字(唯一名字),以及一个从主题到针对这个主题的接收器线程数的映射表来调用 createStream() 方法。
5.2 案例实操
需求:通过SparkStreaming从Kafka读取数据,并将读取过来的数据做简单计算(WordCount),最终打印到控制台。
(1)导入依赖
org.apache.spark
spark-streaming-kafka-0-8_2.11
2.1.1
org.apache.kafka
kafka-clients
0.11.0.2
(2)编写代码
import org.apache.spark.SparkConf
import org.apache.spark.streaming.dstream.{DStream, ReceiverInputDStream}
import org.apache.spark.streaming.kafka.KafkaUtils
import org.apache.spark.streaming.{Seconds, StreamingContext}
//TODO 从kafka中采集数据
object SparkStreaming04_KafakaSource {
def main(args: Array[String]): Unit = {
//使用SparkStreaming完成WordCount
//Spark配置对象
val sparkConf: SparkConf = new SparkConf().setMaster("local[*]").setAppName("SparkStreaming01_WordCount")
//实时数据分析配置对象
//采集周期:以指定的时间为周期采集实时数据
val streamingContext = new StreamingContext(sparkConf,Seconds(5))
//从kafka中采集数据
val kafkaDStream: ReceiverInputDStream[(String, String)] = KafkaUtils.createStream(
streamingContext,
"hdp-1:2181",
"zpark",
Map("zpark" -> 3)
)
//将我们采集的数据进行分解(扁平化)
val wordDStream: DStream[String] = kafkaDStream.flatMap(t=>t._2.split(" "))
//将数据进行结构的转换方便统计分析
val mapDStream: DStream[(String, Int)] = wordDStream.map((_,1))
//将转换结构后的数据进行聚合处理
val wordToSumDStream: DStream[(String, Int)] = mapDStream.reduceByKey(_+_)
//将结果打印出来
wordToSumDStream.print()
//不能停止采集程序,所以不要加stop
//启动采集器
streamingContext.start()
//Drvier等待采集器的执行
streamingContext.awaitTermination()
}
}