关于Spark的structured Streaming窗口函数实时处理kafka消息的简单应用例子

本文详细介绍如何使用Spark Structured Streaming处理实时数据流,包括从Kafka读取数据、数据处理及聚合,以及将处理后的数据输出到控制台或MySQL数据库的具体步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.简单介绍

我在写这个博客的时候spark已经出到2.4.0的版本了,在基础的板块里面spark官网上有strucrtred Streaming的应用。有兴趣的话可以去官网上去看看。

2.话不多说,代码奉上

1.第一步,使用结构的的流读取kafka的消息(这里关于kafka的部分就不多做解释了),

//创建SparkSession
    val spark =
      SparkSession.builder().appName("structured_streamingApp").master("local[*]").getOrCreate()
    spark.sparkContext.setLogLevel("OFF")//由于日志信息太多,设置了一下输出等级

    //使用结构化的流读取卡夫卡中的内容
    import spark.implicits._
    val df = spark.readStream
      .format("kafka")
      .option("kafka.bootstrap.servers", "bigdata1:9092")
      .option("subscribe", "test2")
      .option("startingOffsets", "latest")//注意流式处理没有endingOffset
      .option("includeTimestamp", value = true)//输出内容包括时间戳
      .load()
    val dataSet: Dataset[(String, Timestamp)] = df.selectExpr("CAST(value AS STRING)", "CAST(timestamp AS TIMESTAMP)")
      .as[(String, Timestamp)]//这里定义了消费结构,value表示kafka消息键值对的value数据,timestamp表示当时时间戳。
  1. 将接受到的DataSet[Row]定义字段,以便于下一步的spark-sql操作。
//对kafka的数据进行处理
    val words = dataSet.map(line => {
      val lineArr = line._1.split("\001")
      //将传入的数据根据\001切割然后添加成DataFrame的字段
      (lineArr(0), lineArr(1), lineArr(2), lineArr(3), lineArr(4), lineArr(5).toInt, lineArr(6), lineArr(7), line._2)
    }).toDF("date", "monitorId", "cameraId", "car", "actionTime", "speed", "roadId", "areaId", "timestamp")
  1. 下一步就是定义窗口,在这一步就有两种操作,一个是聚合模式,一个是非聚合的。我在这里演示一下聚合的。
import org.apache.spark.sql.functions._//使用window函数要导入
    val windowedCounts: DataFrame = words.groupBy(
      //根据窗口、卡口id、区域id、道路id分组
      window($"timestamp","300 seconds","5 seconds").alias("windows"), $"monitorId",
      $"roadId", $"areaId"
    ).agg(avg("speed").alias("aspeed"))//根据速度的总和和速度的个数求出平均速度

这里的agg方法有兴趣的可以自己去看一下源码。对DataFrame根据窗口分组后就不能使用像map这样的函数了。只有一下想max、min、avg等这样的函数能使用。

  1. 最后就是对分析后的实时流数据的输出方式,这里我提供了两种简单的方式。
    4.1 输出到控制台
//再将处理好的数据重新写入到kafka中输出
    val query =  windowedCounts.writeStream
      .outputMode("complete")//三种模式:append、complete、update
      //检查点必须设置,不然会报错
      .option("checkpointLocation", "/spark/test/checkpoint_realTime_4")
      .format("console")//控制台输出
      .option("truncate", "false")//是输出的信息是否截断
      .start()

    query.awaitTermination()//等待结束,必须要写。

关于输出模式的参考链接:https://blog.youkuaiyun.com/zeroder/article/details/73650738
4.2 输出导入到mysql

//创建流失数据写入格式对象
    val mysqlSink = new MysqlSink("jdbc:mysql://hostName:3306/tableName", "userName", "pwd")

    //再将处理好的数据重新写入到kafka中输出
    val query =  windowedCounts.writeStream
      .outputMode("complete")//三种模式:append、complete、update
      //检查点必须设置,不然会报错
      .option("checkpointLocation", "/spark/test/checkpoint_realTime_4")
      .foreach(mysqlSink)//输出到mysql
      .start()
    query.awaitTermination()//等待结束

注意:因为structred streaming暂时没有直接提供mysql的输出格式,我们就要自己定义一个mysql输出通道类。

import java.sql.{Connection, DriverManager}

import org.apache.spark.sql.{ForeachWriter, Row}

/**
* mysql输出类
*/
class MysqlSink(url: String, userName: String, pwd: String) extends ForeachWriter[Row]{
  //创建连接对象
  var conn: Connection = _

  //建立连接
  override def open(partitionId: Long, version: Long): Boolean = {
    Class.forName("com.mysql.jdbc.Driver")
    conn = DriverManager.getConnection(url, userName, pwd)
    true
  }

  //数据写入mysql
  override def process(value: Row): Unit = {
    val p = conn.prepareStatement("replace into traffic(windows, monitorId, roadId, areaId, aspeed) values(?,?,?,?,?)")
    p.setString(1, value(0).toString)
    p.setString(2, value(1).toString)
    p.setString(3, value(2).toString)
    p.setString(4, value(3).toString)
    p.setString(5, value(4).toString)
    p.execute()
  }

  //关闭连接对象
  override def close(errorOrNull: Throwable): Unit = {
    conn.close()
  }
}

写到这里关于实时流的处理就已经结束了,如果小伙伴们还想进行数据可视化,我可以推荐一款贼6的国产可视化工具–echarts。使用非常简单,官网上的api和教程实例什么的也给的非常详细。
相关echarts方案的实例传送门。
https://blog.youkuaiyun.com/Apache_Jerry/article/details/86549429

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值