StructuredStreaming知识总结

一、StructuredStreaming的简介

1.1 为什么要引入StructuredStreaming

spark生态系统中的sparkStreaming是一个micro-batch的准实时计算框架,它也需要一个实时计算框架,因此引入了一个新的模块,就是StructuredStreaming.

1.2 StructuredStreaming是什么

1. StructuredStreaming是一个实时计算框架
2. 是一个基于Spark SQL引擎构建的可伸缩的且具有容错性的实时流处理引擎,使用的数据模型是Dataset。
3. 也可以使用Scala,Java或Python编程语言调用StructuredStreaming的API
4. 内部的优化是SparkSql执行引擎的自动优化策略(logical plan)
5. 可以通过checkpoint和WAL进行数据容错
6. 可以保证exactly once语义
7. 可以理解为StructuredStreaming是sparkStreaming的进化版


hello world
hello world spark
hello world spark
hello world spark
hello world spark
hello world spark

1.3 Dataset与Dataframe、RDD的比较

- `RDD` 的优点
       1.  面向对象的操作方式  
       2.  可以处理任何类型的数据  
- `RDD` 的缺点
		1. 运行速度比较慢, 执行过程没有优化   
		2. `API` 比较僵硬, 对结构化数据的访问和操作没有优化 

- `DataFrame` 的优点

  1.  针对结构化数据高度优化, 可以通过列名访问和转换数据   
  2.  增加 `Catalyst` 优化器, 执行过程是优化的, 避免了因为开发者的原因影响效率 

- `DataFrame` 的缺点

      1. 只能操作结构化数据  
      2. 只有无类型的 `API`, 也就是只能针对列和 `SQL` 操作数据, `API` 依然僵硬 

- `Dataset` 的优点
    1. 结合了 `RDD` 和 `DataFrame` 的 `API`, 既可以操作结构化数据, 也可以操作非结构化数据   
	2. 既有有类型的 `API` 也有无类型的 `API`, 灵活选择 

二、StructuredStreaming的体系结构

2.1 数据模型

可以理解为 Spark 中的数据模型 Dataset 有两种形式, 一种是处理静态批量数据的 Dataset, 一种是处理动态实时流的 Dataset, 这两种 Dataset 之间的区别如下

  • 流式的 Dataset 需要使用 readStream 读取外部数据源并返回, 使用 writeStream 将数据写入外部存储,即StructuredStreaming的数据模型
  • 批式的 Dataset 需要使用 read 读取外部数据源并返回, 使用 write 将数据写入外部存储,即SparkSql的数据模型

如下图所示:
在这里插入图片描述

针对于StructuredStreamming,可以这样理解:

1、可以把流式的数据想象成一个不断增长, 无限无界的表
2、无论是否有界, 全都使用 Dataset 这一套 API
3、通过这样的做法, 就能完全保证流和批的处理使用完全相同的代码, 减少这两种处理方式的差异

2.2 体系结构

Structured Streaming 中负责整体流程和执行的驱动引擎叫做 StreamExecution

StreamExecution 如何工作?
在这里插入图片描述

StreamExecution 分为三个重要的部分

  • Source, 从外部数据源读取数据
  • LogicalPlan, 逻辑计划, 在流上的查询计划(解析,优化,计算)
  • Sink, 对接外部系统, 写入结果
总结
  • StreamExecution 是整个 Structured Streaming 的核心, 负责在流上的查询
  • StreamExecution 中三个重要的组成部分, 分别是 Source 负责读取每个批量的数据, Sink 负责将结果写入外部数据源, Logical Plan 负责针对每个小批量生成执行计划
  • StreamExecution 中使用 StateStore 来进行状态的维护

三、入门案例的演示

3.1 代码演示

package com.qf.sparkstreaming.day03

import org.apache.spark.sql.streaming.OutputMode
import org.apache.spark.sql.{
   Dataset, KeyValueGroupedDataset, SparkSession}

/**
 * StuncturedStreaming的入门案例:进行单词统计
 * 步骤如下:
 * 1. `Socket Server` 等待 `Structured Streaming` 程序连接
 * 2. `Structured Streaming` 程序启动, 连接 `Socket Server`, 等待 `Socket Server` 发送数据
 * 3. `Socket Server` 发送数据, `Structured Streaming` 程序接收数据
 * 4. `Structured Streaming` 程序接收到数据后处理数据
 * 5. 数据处理后, 生成对应的结果集, 在控制台打印
 *
 *
 * 代码实现如下:
 */
object _05StructuredStreamingDemo {
   
    def main(args: Array[String]): Unit = {
   
        //获取SparkSql的上下文对象
        val spark: SparkSession = SparkSession.builder().appName("test1").master("local[*]").getOrCreate()
        spark.sparkContext.setLogLevel("ERROR")
        //接收nc发送过来的数据
        import spark.implicits._
        val ds: Dataset[String] = spark.readStream   //是一个实时的读取流对象,因此不会只读一次
          .format("socket")
          .option("host", "qianfeng01")
          .option("port", 10087)
          .load().as[String]
        //数据处理
        val value: KeyValueGroupedDataset[String, (String, Int)] = ds.flatMap(_.split(" ")).map((_, 1)).groupByKey(_._1)
        //统计一个key中有多少对的(key,1)
        val value1: Dataset[(String, Long)] = value.count()
        
        //StruncturedStreaming必须使用writeStream.start()来执行
        value1.writeStream
          /**
           * OutputMode.Complete():全局的数据流进行汇总,此模式一定要在聚合时才能应用
           * OutputMode.APPEND():只会将新数据追加到接收器中,不能用于带有聚合的查询,是默认的
           * OutputMode.UPDATE():只会将更新的数据添加到接收器中,如果没有聚合操作,相当于APPEND
           */
          .outputMode(OutputMode.Complete())
          .format("console")
          .start()      //启动数据流计算程序
          .awaitTermination()   //防止没有数据产生时,停止程序
    }
}

3.2 WordCount 的原理

在这里插入图片描述

整个计算过程大致上分为如下三个部分
    1、Source, 读取数据源
    2、Query, 在流式数据上的查询
    3、Result, 结果集生成

整个的过程如下
    1、随着时间段的流动, 对外部数据进行批次的划分
    2、在逻辑上, 将缓存所有的数据, 生成一张无限扩展的表, 在这张表上进行查询
    3、根据要生成的结果类型, 来选择是否生成基于整个数据集的结果
总结
  • Dataset 不仅可以表达流式数据的处理, 也可以表达批量数据的处理
  • Dataset 之所以可以表达流式数据的处理, 因为 Dataset 可以模拟一张无限扩展的表, 外部的数据会不断的流入到其中
    在这里插入图片描述

3.3 输出模式的介绍

OutputMode.Append:  追加模式,表示当DataFrame/Dataset中有新数据时,可以被写入到sink里。注意,前提是中间过程中没有聚合操作
OutputMode.Complete : 完全模式,表示当DataFrame/Dataset中有新数据时,所有行都被写入到sink里,注意,前提是中间过程中有聚合操作
OutputMode.Update: 更新模式,表示当DataFrame/Dataset中有数据更新时,只有更新的数据被写入到sink里。如果没有聚合操作,相当于Append

四、StructuredStreming的Source

4.1 读取HDFS上的json文件

4.1.1 说明

因为在生产环境中,有些数据是源源不断生产,并保存到HDFS上的,可以会产生很多很多的小文件,所以我们就可以直接使用StructuredStreaming对新产生的文件进行监听并读取,然后直接进行计算。

下面主要使用就是python脚本来模拟持续不断产生新的小文件,StructuredStreaming监听并直接计算的过程。

4.1.2 流程介绍:

第一步)使用python脚本来模拟向hdfs上写入大量的小文件

- `Python` 是解释型语言, 其程序可以直接使用命令运行无需编译, 所以适合编写快速使用的程序, 很多时候也使用 `Python` 代替 `Shell`
- 使用 `Python` 程序创建新的文件, 并且固定的生成一段 `JSON` 文本写入文件
- 在真实的环境中, 数据也是一样的不断产生并且被放入 `HDFS` 中, 但是在真实场景下, 可能是 `Flume` 把小文件不断上传到 `HDFS` 中, 也可能是 `Sqoop` 增量更新不断在某个目录中上传小文件

第二步)使用 Structured Streaming 汇总数据

- `HDFS` 中的数据是不断的产生的, 所以也是流式的数据
- 数据集是 `JSON` 格式, 要有解析 `JSON` 的能力
- 因为数据是重复的, 要对全局的流数据进行汇总和去重, 其实真实场景下的数据清洗大部分情况下也是要去重的

第三步)使用控制台展示数据

- 最终的数据结果以表的形式呈现
- 使用控制台展示数据意味着不需要在修改展示数据的代码, 将 `Sink` 部分的内容放在下一个大章节去说明
- 真实的工作中, 可能数据是要落地到 `MySQL`, `HBase`, `HDFS` 这样的存储系统中

4.1.3 程序测试

1)编写代码
package com.qf.sparkstreaming.day04

import org.apache.spark.sql.streaming.OutputMode
import org.apache.spark.sql.{
   DataFrame, Dataset, Row, SparkSession}
import org.apache.spark.sql.types.{
   DataTypes, StructField, StructType}

/**
 * structuredStreaming从hdfs上读取数据
 */
object _01SourceHDFS {
   
    def main(args: Array[String]): Unit = {
   
        val session: SparkSession = SparkSession.builder()
        .appName("test1")
        .master("local[*]")
        .getOrCreate()
        
        session.sparkContext.setLogLevel("ERROR")

        //为读取到的数据维护一个元数据schema.
        val schemahdfs = StructType(Array(
            StructField("name",DataTypes.StringType),
            StructField("age",DataTypes.IntegerType)
        ))
        //读取数据
        val ds: DataFrame = session.readStream
          .schema(schemahdfs)  //设置表头的元数据
          .json("hdfs://qianfeng01/structure")  //指定hdfs的路径,读取json文件,这个目录必须提前存在
        //进行数据处理,去重
        //val ds: Dataset[Row] = ds.distinct()

        ds.writeStream
          .outputMode(OutputMode.Append())
          .format("console").start()
          .awaitTermination()
    }
}
2)使用python脚本模拟实时上传文件到hdfs上

比如文件名:files.py 位置:/root下

import os

for index in range(10):
    content = """
    {"name":"Michael"}
    {"name":"Andy", "age":30}
    {"name":"Justin", "age":19}
    """

    file_name = "/root/text{0}.json".format(index)

    with open(
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值