Spark Streaming概述
概述
http://spark.apache.org/docs/latest/streaming-programming-guide.html
Spark Streaming是Spark Core扩展(RDD),可以对实时流数据进行可靠、高吞吐、容错的流数据处理。
- ① 构建数据源: Spark Streaming在计算时,输入数据(数据源Sources)可以有多种类型,如:Kafka【重点】、Flume、TCP;
- ② 通过Streaming中提供高阶函数,如map、reduceByKey、join、window等,对流数据进行处理
- ③ 将流数据处理结果写出到存储系统(FS、DB)或者仪表盘(数据展示系统)
批数据&流数据
- 批数据(batch): 有界、范围的数据,通常按照某种规则(时间、文件大小等)将数据划分为一个个批次数据
- 流数据(stream): 无界、没有范围的数据;通常是有起始,永远不会有结束。 类似于水流
Spark Streaming会将接受的输入流数据划分一个个的微批(Micro Batch)数据,这些微批的数据经过Spark引擎的计算处理后,得到微批的结果流并写出到外部系统
结论: Spark Streaming 本质上在微批操作,在批的基础之上实现流数据的处理。
专业术语
DStream( discretized stream)
离散数据流, Spark Streaming 中核心抽象,代表是连续的数据流。类似于批RDD,只不过它代表的是流
DStream本质上是一个RDD的序列 DStream <===> RDD Seq
Streaming入门
- 流数据版的wordcount, 实时统计单词出现次数
导入Streaming依赖
<dependencies>
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-core_2.11</artifactId>
<version>2.4.4</version>
</dependency>
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-streaming_2.11</artifactId>
<version>2.4.4</version>
<!--集群中运行打开,本地运行注释-->
<!--<scope>provided</scope>-->
</dependency>
</dependencies>
开发流处理应用
import org.apache.spark.SparkConf
import org.apache.spark.streaming.{Seconds, StreamingContext}
/**
* 流处理应用
*/
object WordCountApplication {
def main(args: Array[String]): Unit = {
val conf = new SparkConf().setMaster("local[*]").setAppName("streaming wordcount")
val ssc = new StreamingContext(conf, Seconds(5)) // 流数据每隔5秒 划分一个micro batch
// 构建DStream
// 通过获取TCP 套接字的请求数据,用以构建DStream
val dStream = ssc.socketTextStream("SparkOnStandalone", 7777)
// 对流数据应用计算操作
dStream
.flatMap(_.split(" "))
.map((_, 1))
.reduceByKey(_ + _)
.print() // 打印计算结果
// 启动流处理应用
ssc.start()
// 优雅关闭流处理应用
ssc.awaitTermination()
}
}
Streaming计算模型
DStream(离散流)是SparkStreaming是一个基本抽象,代表是一个持续或者连续流数据。DStream可以通过Source或者转换操作创建,本质上DStream是一个RDD有序序列,每一个RDD代表的是DStream中的一个微批。
对于DStream任意操作,都会转换为底层的RDD的操作。比如,单词计数案例,对DStream中每一行数据应用flatMap操作,这个操作将会应用给所有的微批RDD,最后返回一个新的DStream。
构建DStream
DStream通过外部数据源创建的方式有两类Basic(基本,不依赖于第三方jar包)和Advanced(高级,依赖外部的第三方jar包)
Basic Sources
- 通过TCP服务构建
// 通过获取TCP 套接字的请求数据,用以构建DStream
val dStream = ssc.socketTextStream("SparkOnStandalone", 7777)
- FileSystem
//val dStream = ssc.textFileStream(path)
val dStream = ssc.textFileStream("hdfs://SparkOnStandalone:9000/data")
注意:
- path为数据存放目录,而不是具体的数据文件
- spark应用的时间需要和文件系统的时间同步
[root@SparkOnStandalone ~]# date -s '2019-11-29 15:58:30'
2019年 11月 29日 星期五 15:58:30 CST
[root@SparkOnStandalone ~]# clock -w
- Queue RDD
// queue rdd
val rdd1 = ssc.sparkContext.makeRDD(List("Hello Scala", "Hello Streaming"))
val rdd2 = ssc.sparkContext.makeRDD(List("Hello Scala2", "Hello Streaming2"))
val rdd3 = ssc.sparkContext.makeRDD(List("Hello Scala3", "Hello Streaming3"))
// 构建rdd队列
val queue = scala.collection.mutable.Queue(rdd1, rdd2, rdd3)
val dStream = ssc.queueStream(queue)
Advanced Sources
Kafka Source
afka source + spark streaming的集成
准备工作:
- kafka服务正常
[root@HadoopNode00 zookeeper-3.4.6]# bin/zkServer.sh start conf/zk.cfg
[root@HadoopNode00 kafka_2.11-0.11.0.0]# bin/kafka-server-start.sh -daemon config/server.properties
2.准备流数据Topic
[root@HadoopNode00 kafka_2.11-0.11.0.0]# bin/kafka-topics.sh --create --topic lqq--partitions 1 --replication-factor 1 --zookeeper HadoopNode00:2181
Created topic "baizhi
3.准备kafka topic生产者
用以提供流数据
[root@HadoopNode00 kafka_2.11-0.11.0.0]# bin/kafka-console-producer.sh --broker-list HadoopNode00:9092 --topic lqq
- 导入集成依赖
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-streaming-kafka-0-10_2.11</artifactId>
<version>2.4.4</version>
</dependency>
- 开发流数据处理应用
package sources
import org.apache.kafka.clients.consumer.ConsumerConfig
import org.apache.kafka.common.serialization.StringDeserializer
import org.apache.spark.SparkConf
import org.apache.spark.streaming.kafka010.{KafkaUtils, LocationStrategies}
import org.apache.spark.streaming.{Seconds, StreamingContext}
import org.apache.spark.streaming.kafka010.ConsumerStrategies.Subscribe
/**
* 流处理应用
*/
object WordCountApplicationForKafkaSource {
def main(args: Array[String]): Unit = {
val conf = new SparkConf().setMaster("local[*]").setAppName("streaming wordcount")
val ssc = new StreamingContext(conf, Seconds(5)) // 流数据每隔5秒 划分一个micro batch
// 提高日志输出级别
ssc.sparkContext.setLogLevel("ERROR")
// 声明kafka的连接参数
val params = Map(
(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "HadoopNode00:9092"), // kafka集群地址,HadoopNode00配置IP映射
(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, classOf[StringDeserializer]),
(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, classOf[StringDeserializer]),
(ConsumerConfig.GROUP_ID_CONFIG, "g1") // kafka 同组负载均衡 不同组广播
)
// 构建基于kafka source的dStream
val dStream = KafkaUtils
.createDirectStream[String, String](
ssc,
LocationStrategies.PreferConsistent, // 位置策略【优化相关】 大多数情况适用优先一致分配方法 将Kafka Topic中数据平均方式分配多个Executor
Subscribe[String, String](List("baizhi"), params)
)
dStream
.map(record => (record.key(), record.value(), record.partition(), record.offset(), record.topic(), record.timestamp()))
.print()
// 启动流处理应用
ssc.start()
// 优雅关闭流处理应用
ssc.awaitTermination()
}
}
- 启动kafka生产者
[root@HadoopNode00 kafka_2.11-0.11.0.0]# bin/kafka-console-producer.sh --broker->Hello Scala
>Hello Hadoop
>Hello Hello
>Hello
>
Flume
flume + spark streaming集成
准备工作:
1.准备flume配置文件
# source \ channel \sink
a1.sources = r1
a1.sinks = avroSink
a1.channels = c1
a1.sources.r1.type = netcat
a1.sources.r1.bind = HadoopNode00
a1.sources.r1.port = 6666
a1.channels.c1.type = memory
a1.sources.r1.channels = c1
a1.sinks.avroSink.type = avro
a1.sinks.avroSink.channel = c1
a1.sinks.avroSink.hostname = 192.168.126.1
a1.sinks.avroSink.port = 9999
- 导入集成依赖
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-streaming-flume_2.11</artifactId>
<version>2.4.4</version>
</dependency>
- 开发流数据处理应用
import org.apache.spark.streaming.flume._
val flumeStream = FlumeUtils.createStream(streamingContext, [chosen machine's hostname], [chosen port])
- 启动Flume Agent和Streaming应用