目录
01:上篇回顾
https://blog.csdn.net/m0_57498038/article/details/119113968
-
DStream的函数有哪些?
-
转换函数:DStream调用转换函数返回一个新的DStream
-
transform:取DStream中每个RDD进行处理,有返回值
-
-
输出函数:主要用于输出保存程序的结果
-
foreachRDD:取DStream中每个RDD进行处理,没有返回值
-
-
-
流式计算的三种计算模式及应用场景是什么?
-
无状态模式:当前批次的计算结果就是当前批次的最终结果,与前面的批次是没有关系的
-
处理数据的范围:当前批次
-
应用场景:ETL
-
-
有状态模式:当前批次的最终结果来自于当前批次的计算结果与前面批次的最终结果进行聚合得到的
-
处理数据的范围:前面所有批次的结果
-
应用场景:所有数据的聚合:统计累计总成交额
-
-
窗口计算模式:对固定窗口大小范围的数据进行聚合
-
处理数据的范围:某个时间段内的数据
-
应用场景:固定处理近期的某个时间范围数据聚合结果:每3s处理近6s的数据
-
1s:批次数据
-
3s:滑动时间
-
6s:窗口大小
-
-
-
-
SparkStreaming集成Kafka的方式和原理是什么?
-
方式
-
Receiver模式
-
Direct模式
-
-
==Direct模式的原理==
-
基本原理:使用Kafka消费者的拉取数据的模式,使用Kafka Consumer的Simple API
-
实现流程
-
step1:没到达一个批次时间,SparkStreaming根据订阅消费的Topic的每个分区到Kafka获取每个分区的最新的offset
-
step2:根据上一次消费的最后的一个Offset【不包含】,与当前这个分区的最新offset,组合一个当前批次要处理的数据范围
-
[fromOffset,utilOffset)
-
-
step3:向Kafka请求本次消费数据的范围的数据,获取这个批次的数据进行处理
-
-
优点
-
简单的并行化模式:Kafka中的1个分区= RDD的1个分区
-
对性能的影响很小:选择拉取模式,不用使用WAL
-
保证一次性语义:允许自己管理offset
-
-
-
-
SparkStreaming程序怎么做容灾?
-
Offset的安全性和顺序性的解决方案:自己管理offset存储
-
step1:消费处理成功,自己将utilOffset存储在外部系统
-
step2:消费故障重启,自己从外部存储中读取上一次正常消费的offset位置,向Kafka提交消费请求
-
-
有状态计算,必须保存之前的状态的结果,如果消费者程序故障,程序重启,不知道上一次的处理的结果,只能重头开始?
-
基本方案:将上一次状态存储在CHK中
ssc.checkpoint
-
问题:如果每次重新构建一个ssc,不知道上一次的chk是谁,不能恢复上一次的数据?
-
解决:第一次,构建一个新的ssc,从第二次开始,从chk中恢复ssc
val ssc = StreamingContext.getActiveOrCreate( chkdir,//用于从第二次开始恢复ssc //第一次运行,构建一个新的,配置chk () => { new StreamingContext.checkpoint(chkdir) } )
-
chk作用
-
程序元数据:程序的所有配置信息、DStream所有转换逻辑、未处理的批次信息
-
数据的记录:状态数据的结果
-
-
-
02:本篇内容
-
StructStreaming基本介绍
-
基本设计:SparkStreaming的缺点
-
功能、应用场景
-
为什么需要StructStreaming,它的优点是什么?
-
-
==StructStreaming的使用==
-
驱动接口:SparkSession
-
数据抽象:DataSet
-
开发方式:DSL 、SQL
-
数据源
-
Source:Kafka
-
Sink:Kafka、MySQL、Redis
-
-
物理网设备分析的案例:DSL和SQL
-
-
了解一些其他特性
-
流式数据的去重
-
持续数据处理:真实时计算
-
SparkStreaming:准实时,微小时间的批处理来模拟实时
-
StructStreaming:真实时,产生一条处理一条
-
离线
-
准实时
-
真实时
-
-
-
EventTime:基于数据的事件时间的处理
-
现在的处理都是基于处理时间的结果
-
数据产生的时间【事件时间】与数据处理的时间是否一样?
-
-
03:SparkStreaming的缺点
-
目标:了解SparkStreaming的设计缺点
-
实施
-
缺点1:对于数据分析业务而言,编程复杂,底层都是RDD的函数式编程
-
数据分析最适合的语言:SQL
-
SparkStreaming不支持SQL:RDD的函数式编程
-
-
缺点2:批流代码不统一
-
离线计算:SparkSQL:DSL【函数式编程】、==支持SQL==
-
实时计算:SparkStreaming==不支持SQL==
-
-
缺点3:很难支持流式应用端到端精确性一次语义,需要自己保证Input和Output的Exactly Once
-
只能保证DStream自己的一次性语义
-
-
缺点4:使用的是Processing Time而不是Event Time
-
Processing Time:数据的处理时间的计算
-
Event Time:事件时间,数据产生时间的计算,更精确
-
-
解决:流式计算一直没有一套标准化、能应对各种场景的模型,直到2015年Google发表了The
Dataflow
Model的论文( https://yq.aliyun.com/articles/73255 )。
-
-
小结
-
了解SparkStreaming的设计缺点
-
04:StructStreaming的设计
-
目标:了解StructStreaming的设计和功能
-
实施
-
设计
-
==DataFlow论文==:提出了一种新的流式计算模型,用统一化的流式计算代替所有分布式计算,提出一些新的概念:事件时间
-
Spark模块
-
SparkCore:基于代码的离线开发
-
SparkSQL:基于SQL的离线开发
-
SparkStreaming:基于代码的实时计算【本质上还是SparkCore的RDD的编程】
-
基于SparkCore而设计的
-
DStream = Seq【RDD】
-
-
StructStreaming:基于SQL的实时计算
-
基于SparkSQL而设计的
-
DataFrame/DateSet:无边界表
-
数据源源不断的放入无边界的表中
-
通过SQL或者DSL对无边界的表进行计算
-
将结果放入无边界的结果表中
-
通过一个查询器【Query】,根据需求输出对应的结果
-
-
-
-
目的:统一化开发接口和模块,用一个SparkSQL模块来实现离线和实时计算
-
-
官方介绍
Structured Streaming is a scalable and fault-tolerant stream processing engine built on the Spark SQL engine. You can express your streaming computation the same way you would express a batch computation on static data. #你可以使用批处理的开发方式来开发流式计算的程序:可以直接使用之前sparkSQL开发来开发StructStreaming The Spark SQL engine will take care of running it incrementally and continuously and updating the final result as streaming data continues to arrive. #可以使用SparkSQL对源源不断到达的数据进行持续化或者更新化的数据处理 You can use the Dataset/DataFrame API in Scala, Java, Python or R to express streaming aggregations, event-time windows, stream-to-batch joins, etc. #你可以使用DSL函数式编程来实现数据的计算 The computation is executed on the same optimized Spark SQL engine. Finally, the system ensures end-to-end exactly-once fault-tolerance guarantees through checkpointing and Write-Ahead Logs. #通过chk和wal来保证程序的健壮性 In short, Structured Streaming provides fast, scalable, fault-tolerant, end-to-end exactly-once stream processing without the user having to reason about streaming. #简单点说,用户不需要关心流式计算实现的原理可以实现快速的可扩展高容错的一次性语义的流式计算程序
-
功能
-
功能:实现分布式的结构化数据的实时流式计算
-
基于SparkSQL实现的:将流式计算的应用底层通过sparkSQL来实现
-
-
应用
-
主要用于Spark实现结构化数据的流式计算的处理
-
-
流程
-
step1:读取数据流,将数据流放入一个无边界的数据表中【DataSet】
val ds = spark.readStream.format.load
-
step2:调用DSL或者SQL对DS中的数据进行转换计算,底层还是批处理
-
step3:每次追加计算的结果,放入一个无边界的结果表,不断更新计算的结果
-
step4:根据需求,构建一个查询器,从无边界的结果表中输出我们计算的结果即可
-
-
-
小结
-
了解StructStreaming的设计和功能
-
05:官方示例WordCount
-
目标:实现官方示例程序WordCount的测试
-
实施
-
启动HDFS
start-dfs.sh
-
第一台机器运行nc
nc -lk 9999
-
第二台机器运行
/export/server/spark/bin/run-example \ --master local[2] \ --conf spark.sql.shuffle.partitions=2 \ org.apache.spark.examples.sql.streaming.StructuredNetworkWordCount \ node1 9999
-
观察输出
-
没有新的数据,不输出的
-
默认实现了有状态的计算
-
默认时间间隔:默认是没有时间间隔,只要有数据默认是上一个批次执行结束,就执行下一个批次
-
是可以指定时间的规则
-
按照时间间隔:每1s执行一次,跟SparkStreaming是一样的
-
只执行一个批次:就是批处理
-
持续数据处理:不断对数据进行监听处理,不再是微小时间的批处理,真实时计算
-
-
-
-
-
小结
-
实现官方示例程序WordCount的测试
-
06:自定义开发WordCount实现
-
目标:实现StructStreaming自定义开发WordCount程序
-
实施
-
代码开发
package bigdata.spark.struct.wordcount import org.apache.spark.sql.streaming.OutputMode import org.apache.spark.sql.{DataFrame, SparkSession} /** * @ClassName StructWordCount * @Description TODO 使用结构化流计算构建Wordcount程序 */ object StructWordCount { def main(args: Array[String]): Unit = { //todo:1-构建SparkSession val spark = SparkSession .builder() .appName(this.getClass.getSimpleName.stripSuffix("$")) .master("local[2]") .config("spark.sql.shuffle.partitions",2) .getOrCreate() //日志级别 spark.sparkContext.setLogLevel("WARN") //导包 import spark.implicits._ import org.apache.spark.sql.functions._ //todo:2-处理数据 //step1:读取数据 val inputData: DataFrame = spark.readStream .format("socket") .option("host","node1") .option("port",9999) .load() //step2:处理数据 val rsData = inputData .as[String] .filter(line => line != null && line.trim.length > 0) .select(explode(split($"value","\\s+")).as("word")) .groupBy($"word") .count() .withColumnRenamed("count","cnt") //step3:保存结果:查询器 val query = rsData .writeStream .outputMode(OutputMode.Complete()) //指定输出模式 .format("console") //输出的类型 .start() //流式计算的启动,类似于离线计算的save //todo:3-启动并持久运行 query.awaitTermination() //持久运行 query.stop() //释放资源 } }
-
结果
-
与SparkSQL离线的差异
-
输入:调用readStream方法
-
数据源:实时数据源
-
-
-
处理:基本一致
-
输出
-
writeStream:构建输出流
-
outputMode:输出模式,有状态和无状态计算
-
start:构建查询器
-
awaitTermination:持续运行
-
-
-
-
小结
-
实现StructStreaming自定义开发WordCount程序
-
07:Source数据源类型及File Source
-
目标:了解StructStreaming的数据源类型及File Source的实现
-
路径
-
step1:支持Source数据源
-
step2:File Source
-
-
实施
-
支持Source数据源
-
地址:http://spark.apache.org/docs/2.4.5/structured-streaming-programming-guide.html#input-sources
-
File:动态读取文件
-
Kafka:最常用
-
Socket:监听端口,用于测试,不能用于生产环境,不支持故障恢复
The socket source should not be used for production applications! It does not support recovery.
-
常用属性
host: host to connect to, must be specified port: port to connect to, must be specified
-
-
Rate:用于模拟数据做测试的,一般也不用
-
-
File Source
-
功能:动态的监听一个目录,如果目录中出现的新的文件,就会被动态的读取数据的内容
-
Flume:Spooldir
-
-
应用场景:一般用于监听日志文件直接采集处理,用的比较少
-
需求:监听一个信息目录,统计年龄小于25岁的人的兴趣爱好的分布
-
结果:对所有年龄小于25岁的人统计每种兴趣爱好的人数
hobby count running 3 swimming 1
-
代码
-
动态监听信息目录
/** * 实现监听目录读取文件,注意:所有filesource必须指定schema */ //为当前文件创建schema val schema = new StructType() .add("name",StringType,true) .add("age",IntegerType,true) .add("hobby",StringType,true) val inputStream: DataFrame = spark.readStream .format("csv") .option("sep", ";") .schema(schema) //所有file source必须指定文件内容的schema .load("datastruct/filesource")//给定监听的目录的地址
-
实现转换
//todo:2-处理数据:对所有年龄小于25岁的人统计每种兴趣爱好的人数 val rsStream = inputStream .filter( $"age" < 25) .groupBy($"hobby") .count()
-
输出
val query = rsStream //保存数据流 .writeStream .outputMode(OutputMode.Complete()) //指定查询输出的模式 .format("console")//指定保存的模式 .option("numRows","10") //打印多少行 .option("truncate","false") //是否省略显示 .start() //启动流式计算,构建查询器
-
结果
-
-
-
-
-
小结
-
了解StructStreaming的数据源类型及File Source的实现
-