Spark-Streaming缓存计算结果,Wordcount累加求和

本文探讨了Apache Spark Streaming在实时数据处理中的应用,重点介绍了updateStateByKey函数用于数据累加,以及如何将中间数据存储到MySQL和Redis中,确保应用重启后能恢复上次的计算结果。

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

依赖

<dependencies>
        <dependency>
            <groupId>org.apache.spark</groupId>
            <artifactId>spark-streaming_2.11</artifactId>
            <version>2.1.1</version>
        </dependency>
        <dependency>
            <groupId>org.apache.spark</groupId>
            <artifactId>spark-streaming-kafka-0-8_2.11</artifactId>
            <version>2.1.1</version>
        </dependency>
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>2.9.0</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.27</version>
        </dependency>
    </dependencies>

一 updateStateByKey

函数声明:

def updateStateByKey[S: ClassTag](
                 updateFunc: (Seq[V], Option[S]) => Option[S]): DStream[(K, S)]

getActiveOrCreate 函数声明

def getActiveOrCreate(
      checkpointPath: String,
      creatingFunc: () => StreamingContext,
      hadoopConf: Configuration = SparkHadoopUtil.get.conf,
      createOnError: Boolean = false
    ): StreamingContext = {
    ACTIVATION_LOCK.synchronized {
      getActive().getOrElse { getOrCreate(checkpointPath, creatingFunc, hadoopConf, createOnError) }
    }
  }

需求:对上次计算的结果进行缓存,在应用重启后,加载上次计算的结果,这里从scoket中读取流数据

package com.gc.sparkStreaming.day01.transform

import org.apache.spark.streaming.dstream.{DStream, ReceiverInputDStream}
import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.streaming.{Seconds, StreamingContext}

object RddToDStream {
  //定义状态改变函数 seq:Seq[Int] 当前批次的数据  option:Option[Int] 累加值
  val updateFun=(seq:Seq[Int],option:Option[Int])=>{
    Some(seq.sum+option.getOrElse(0))
  }
  val checkPoint:String="./ssss" // checkpoint 目录 默认取的是core-default.xml中fs.defaultFS 的文件历经

  // 创建StreamingContext 函数
  val createStream: () => StreamingContext = ()=>{
    val conf = new SparkConf().setAppName("rdd").setMaster("local[*]")
    val spark: StreamingContext = new StreamingContext(conf,Seconds(2))
    val dStrem: ReceiverInputDStream[String] = spark.socketTextStream("hadoop103",9999)
    val ds: DStream[(String, Int)] = dStrem.flatMap(_.split("\\W+")).map((_,1))
    val res: DStream[(String, Int)] = ds.updateStateByKey(updateFun)
    res.print() // 调用行动算子
    spark
  }
  def main(args: Array[String]): Unit = {
    val spark = StreamingContext.getActiveOrCreate(checkPoint,createStream)
    spark.checkpoint("./ssss")
    spark.start()
    spark.awaitTermination()
  }
}

注意点:

  • 使用updateBykey 必须设置定checkpoint
  • 要想在应用重启后保留上次计算的结果,需使用这种方式创建StreamingContext对象StreamingContext.getActiveOrCreate(checkPoint,createStream)
  • 使用上述方式创建StreamingContext后,必须将业务逻辑全部放在createStream函数内,否则会报错

二 中间数据存储到Mysql

累加的本质就是获取之前运算的结果和本次新传入的数据进行汇总,要做的就是将之前累加后的数据缓存起来

import java.sql.{Connection, DriverManager, PreparedStatement}
import java.util.Properties

import org.apache.spark.SparkConf
import org.apache.spark.streaming.dstream.DStream
import org.apache.spark.streaming.{Seconds, StreamingContext}

object UseMysqlToChecpoint {
  def main(args: Array[String]): Unit = {
    val conf = new SparkConf().setMaster("local[*]").setAppName("UseMysqlToChecpoint")
    val spark = new StreamingContext(conf,Seconds(2))
    //业务逻辑
    // 从scoket
    val sourceDstream = spark.socketTextStream("hadoop102",9999)
    val wordCount: DStream[(String, Int)] = sourceDstream.flatMap(_.split("\\W+")).map((_,1)).reduceByKey(_+_)
     //将本次计算的结果写入mysql
    wordCount.foreachRDD(rdd=>{

    rdd.foreachPartition(it=>{
      val conn = getConnection() // 一个分区创建一个连接
      conn.setAutoCommit(false) //取消自动提交事务
      val sql:String="insert into  wordcount values(?,?) on duplicate key update wcount=if(wcount is not null ,wcount+?,wcount)" //设置wordcount 表中对应的work为主键,不允许重复,当key存在的时候进行修改,if(wcount is not null ,wcount+?,wcount) 判断统计结果是否存在,存在则加上本次计算的结果,不存在则直接修改
      val ps: PreparedStatement = conn.prepareStatement(sql)
      it.foreach(result=>{
          ps.setString(1,result._1);
          ps.setInt(2,result._2);
           ps.setInt(3,result._2);
          ps.addBatch() // 一批
        })
      ps.executeBatch() // 批量执行
      conn.commit() // 提交事务
      ps.close() // 关闭连接
      conn.close()
    })

    })
    spark.start()
    spark.awaitTermination()
  }

def getConnection():Connection={
  Class.forName("com.mysql.jdbc.Driver")
  val pro = new Properties()
  pro.setProperty("user","root")
  pro.setProperty("password","root")
  val conn = DriverManager.getConnection("jdbc:mysql://hadoop102:3306/sparkdemo",pro)
  conn
}
}

运行结果

  • 连接外部存储应该放在foreachPartition中,一个分区创建一个连接
  • 在这为方便起见,将单词的word作为mysql表中的主键
  • 可以使用ps.addBatch() 和 ps.executeBatch()对一个分区的数据进行批量执行

三 中间数据存到Redis中

import org.apache.spark.SparkConf
import org.apache.spark.streaming.dstream.DStream
import org.apache.spark.streaming.{Seconds, StreamingContext}
import redis.clients.jedis.{JedisPool, JedisPoolConfig}

object UseRedisToChecpoint {
  private val jedisPoolConfig: JedisPoolConfig = new JedisPoolConfig()
  jedisPoolConfig.setMaxTotal(100) //最大连接数
  jedisPoolConfig.setMaxIdle(20) //最大空闲
  jedisPoolConfig.setMinIdle(20) //最小空闲
  jedisPoolConfig.setBlockWhenExhausted(true) //忙碌时是否等待
  jedisPoolConfig.setMaxWaitMillis(500) //忙碌时等待时长 毫秒
  jedisPoolConfig.setTestOnBorrow(false) //每次获得连接的进行测试
  private val jedisPool: JedisPool = new JedisPool(jedisPoolConfig, "hadoop102", 6379)


  def main(args: Array[String]): Unit = {
    val conf = new SparkConf().setMaster("local[*]").setAppName("UseRedisToChecpoint")
    val spark = new StreamingContext(conf,Seconds(2))
    //业务逻辑
    val sourceDstream = spark.socketTextStream("hadoop102",9999)
    val wordCount: DStream[(String, Int)] = sourceDstream.flatMap(_.split("\\W+")).map((_,1)).reduceByKey(_+_)
     //将本次计算的结果写入redis
    wordCount.foreachRDD(rdd=>{
    rdd.foreachPartition(it=>{
      val jedis = getJedis()
      it.foreach(result=>{
        jedis.hincrBy("wordcount",result._1,result._2); //hincrBy 存在累加 不存在则写入1
      })
      jedis.close()
    })

    })
    spark.start()
    spark.awaitTermination()
  }
  def getJedis()={
    jedisPool.getResource
  }

}

中间结果存储到redis中

<think>首先,用户的问题是:“spark-streaming”,这似乎是关于Spark Streaming的,但信息不完整。用户可能是在询问Spark Streaming的基本概念、用法或其他方面。由于用户说“请使用中文回答问题”,我需要用中文回复。 用户指令强调:在回答完问题后,必须根据用户的问题及我的回答提出3-5个相关问题。相关问题必须以“§§相关问题§§”开头,并输出在末尾。禁止换行分散,意思是整个输出要连贯。 示例格式显示:先回答问题,然后代码块(如果有),然后是“§§相关问题§§”后跟问题列表。 用户的问题是“spark-streaming”,这可能是一个拼写错误或简写,应该是“Spark Streaming”。Spark Streaming是Apache Spark的一个组件,用于实时流处理。 由于用户没有指定具体问题,我假设他们在问Spark Streaming的基本介绍。我应该提供一个简洁的概述。 我的回答结构: 1. 回答问题:用中文解释Spark Streaming是什么。 2. 如果需要,提供代码示例(如示例中所示)。 3. 最后,以“§§相关问题§§”开头,提出3-5个相关问题。 相关问题必须基于用户的问题和我的回答。用户的问题是“spark-streaming”,所以我需要提出相关问题。 可能的回答内容: - 定义Spark Streaming- 简要描述其工作原理。 - 可能提供一个简单的代码示例(如用Python或Scala),因为示例中有代码块。 示例格式中有一个Python代码块,所以我应该包括一个简单的Spark Streaming代码示例。 在Spark Streaming中,常见的代码是用Scala或Python写的。例如,一个简单的WordCount示例。 确保所有内容用中文。 步骤: 1. 回答:解释Spark Streaming。 2. 提供代码示例(可选,但示例中有)。 3. 结束于“§§相关问题§§”后跟问题列表。 相关问题:3-5个,基于主题。例如: - Spark Streaming与其他流处理框架的比较。 - 如何设置Spark Streaming- 常见用例。 - 优化技巧等。 最后,输出必须是连贯的,没有不必要的换行。 草拟回答: - 开头:Spark Streaming是Apache Spark的一个扩展,用于实时数据处理... - 代码:用Python写一个简单的Spark Streaming应用。 - 然后:§§相关问题§§ 1. 第一个问题 2. 第二个 3. 第三个 确保代码块正确格式化。在Markdown中,用三个反引号包围代码。 完整输出: 用户的问题:spark-streaming 我的回答:Spark Streaming 是 Apache Spark 的一个组件,用于处理实时数据流。它允许开发人员使用类似批处理的API来处理流数据,支持高吞吐量和容错性。 然后代码示例: ```python from pyspark.streaming import StreamingContext # 创建StreamingContext,每1秒批处理间隔 ssc = StreamingContext(sparkContext, 1) # 创建一个DStream,监听localhost:9999 lines = ssc.socketTextStream("localhost", 9999) # 分割每行成单词 words = lines.flatMap(lambda line: line.split(" ")) # 计数每个单词 pairs = words.map(lambda word: (word, 1)) wordCounts = pairs.reduceByKey(lambda x, y: x + y) # 打印结果 wordCounts.pprint() ssc.start() # 启动计算 ssc.awaitTermination() # 等待计算终止 ``` 然后:§§相关问题§§ 1. Spark Streaming 与 Kafka 如何集成? 2. 如何处理 Spark Streaming 中的故障恢复? 3. Spark Streaming 的微批处理模式是什么? 4. 有哪些常见的 Spark Streaming 用例? 我需要确保有3-5个问题。 现在,用中文写所有内容。</think>Spark Streaming 是 Apache Spark 的一个扩展模块,专门用于处理实时数据流。它采用微批处理(Micro-Batching)架构,将连续的数据流分割成小批次,然后使用 Spark 的批处理引擎进行处理。这允许开发者使用熟悉的 DataFrame 或 RDD API 来实现高吞吐、低延迟的流式应用,并支持容错性和 Exactly-Once 语义。核心概念包括 DStream(Discretized Stream),它代表一个连续的数据序列,通过转换操作(如 `map`、`reduceByKey`)进行处理。Spark Streaming 常用于日志分析、实时监控和事件处理等场景。 以下是一个简单的 Python 代码示例,展示如何使用 Spark Streaming 实现一个基本的单词计数应用(需要先启动一个本地 Socket 服务器): ```python from pyspark import SparkContext from pyspark.streaming import StreamingContext # 初始化 SparkContext 和 StreamingContext(批处理间隔为1秒) sc = SparkContext("local[2]", "SparkStreamingExample") ssc = StreamingContext(sc, 1) # 创建一个 DStream,监听本地端口 9999 lines = ssc.socketTextStream("localhost", 9999) # 处理数据流:分割单词并计数 words = lines.flatMap(lambda line: line.split(" ")) pairs = words.map(lambda word: (word, 1)) word_counts = pairs.reduceByKey(lambda a, b: a + b) # 打印结果 word_counts.pprint() # 启动流处理 ssc.start() ssc.awaitTermination() # 等待终止 ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Master_slaves

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值