建议先看DStream-04 Window函数的原理和源码

Demo

updateState 可以到达将每次 word count 计算的结果进行累加。

val conf = new SparkConf().setMaster("local[2]").setAppName("NetworkWordCount")
val ssc = new StreamingContext(conf, Seconds(1))
ssc.sparkContext.setLogLevel("WARN")
val lines = ssc.socketTextStream("localhost", 9999)
ssc.checkpoint("/Users/chouc/Work/IdeaProjects/learning/learning/spark/src/main/resources/checkpoint/SocketDstream")
val wordCounts = lines.flatMap(_.split(" ")).map((_,1)).updateStateByKey[Int]((seq:Seq[Int],total:Option[Int])=>{
total match {
case Some(value) => Option(seq.sum + value)
case None => Option(seq.sum)
}
})
wordCounts.print()
ssc.start()
ssc.awaitTermination()

源码

其实想要达到累加还是比较简单。

只要将本次计算的结果 + 上一次计算结果就可以了。

入口就是 updateStateByKey

PairDStreamFunctions

def updateStateByKey[S: ClassTag](
updateFunc: (Iterator[(K, Seq[V], Option[S])]) => Iterator[(K, S)],
partitioner: Partitioner,
rememberPartitioner: Boolean): DStream[(K, S)] = ssc.withScope {
val cleanedFunc = ssc.sc.clean(updateFunc)
val newUpdateFunc = (_: Time, it: Iterator[(K, Seq[V], Option[S])]) => {
cleanedFunc(it)
}
new StateDStream(self, newUpdateFunc, partitioner, rememberPartitioner, None)
}

文章 DStream-04 window 函数时候,提到了。每次计算后,每个DStream 都会将上一次的RDD 放入内存中,以供下一次使用,这样一来也就更简单。如果获取上一次的RDD呢 ,也就是当前batch time 减去 slideDuration 就等于上一个批次的时间戳,可以通过getOrCompute 得到。

slideDuration 默认情况就是 batchInterval 批次间隔时间。在window 中也是批次时间。

StateDStream

class StateDStream[K: ClassTag, V: ClassTag, S: ClassTag](
parent: DStream[(K, V)],
updateFunc: (Time, Iterator[(K, Seq[V], Option[S])]) => Iterator[(K, S)],
partitioner: Partitioner,
preservePartitioning: Boolean,
initialRDD: Option[RDD[(K, S)]]
) extends DStream[(K, S)](parent.ssc) { // 这边注意,这个StateDStream 需要设置checkpoint 地址 来保存数据。
super.persist(StorageLevel.MEMORY_ONLY_SER)
override val mustCheckpoint = true // 这个方法就是将 前一个batch RDD 的结果和当前计算的结果合并
private [this] def computeUsingPreviousRDD(
batchTime: Time,
parentRDD: RDD[(K, V)],
prevStateRDD: RDD[(K, S)]) = {
// Define the function for the mapPartition operation on cogrouped RDD;
// first map the cogrouped tuple to tuples of required type,
// and then apply the update function
val updateFuncLocal = updateFunc
val finalFunc = (iterator: Iterator[(K, (Iterable[V], Iterable[S]))]) => {
val i = iterator.map { t =>
val itr = t._2._2.iterator
val headOption = if (itr.hasNext) Some(itr.next()) else None
(t._1, t._2._1.toSeq, headOption)
}
updateFuncLocal(batchTime, i)
}
// cogroup 合并
val cogroupedRDD = parentRDD.cogroup(prevStateRDD, partitioner)
// 然后将合并后的结果计算
val stateRDD = cogroupedRDD.mapPartitions(finalFunc, preservePartitioning)
Some(stateRDD)
} override def compute(validTime: Time): Option[RDD[(K, S)]] = { // Try to get the previous state RDD
// 算出上一个batch time 来获取上一个batch的RDD。
getOrCompute(validTime - slideDuration) match { //如果有就说明之前有RDD,如果没有则当前是第一个batch
case Some(prevStateRDD) => // If previous state RDD exists
// Try to get the parent RDD
// 获取当前这个批次来的数据 。这边理解有点绕,parent.getOrCompute(validTime) 就是前一个DStream 计算的结果,可以看下MappedDStream 的 方法就比较清楚了。
parent.getOrCompute(validTime) match {
case Some(parentRDD) => // If parent RDD exists, then compute as usual
// 见两个RDD 的数据。
computeUsingPreviousRDD (validTime, parentRDD, prevStateRDD)
case None => // If parent RDD does not exist
// Re-apply the update function to the old state RDD
val updateFuncLocal = updateFunc
val finalFunc = (iterator: Iterator[(K, S)]) => {
val i = iterator.map(t => (t._1, Seq.empty[V], Option(t._2)))
updateFuncLocal(validTime, i)
}
val stateRDD = prevStateRDD.mapPartitions(finalFunc, preservePartitioning)
Some(stateRDD)
} case None => // If previous session RDD does not exist (first input data)
// Try to get the parent RDD
parent.getOrCompute(validTime) match {
case Some(parentRDD) => // If parent RDD exists, then compute as usual
initialRDD match {
case None =>
// Define the function for the mapPartition operation on grouped RDD;
// first map the grouped tuple to tuples of required type,
// and then apply the update function
val updateFuncLocal = updateFunc
val finalFunc = (iterator: Iterator[(K, Iterable[V])]) => {
updateFuncLocal (validTime,
iterator.map (tuple => (tuple._1, tuple._2.toSeq, None)))
} val groupedRDD = parentRDD.groupByKey(partitioner)
val sessionRDD = groupedRDD.mapPartitions(finalFunc, preservePartitioning)
// logDebug("Generating state RDD for time " + validTime + " (first)")
Some (sessionRDD)
case Some (initialStateRDD) =>
computeUsingPreviousRDD(validTime, parentRDD, initialStateRDD)
}
case None => // If parent RDD does not exist, then nothing to do!
// logDebug("Not generating state RDD (no previous state, no parent)")
None
}
}
}
}

DStream-05 updateStateByKey函数的原理和源码的更多相关文章

  1. DStream-04 Window函数的原理和源码

    DStream 中 window 函数有两种,一种是普通 WindowedDStream,另外一种是针对 window聚合 优化的 ReducedWindowedDStream. Demo objec ...

  2. Java并发编程(七)ConcurrentLinkedQueue的实现原理和源码分析

    相关文章 Java并发编程(一)线程定义.状态和属性 Java并发编程(二)同步 Java并发编程(三)volatile域 Java并发编程(四)Java内存模型 Java并发编程(五)Concurr ...

  3. Kubernetes Job Controller 原理和源码分析(二)

    概述程序入口Job controller 的创建Controller 对象NewController()podControlEventHandlerJob AddFunc DeleteFuncJob ...

  4. Kubernetes Job Controller 原理和源码分析(三)

    概述Job controller 的启动processNextWorkItem()核心调谐逻辑入口 - syncJob()Pod 数量管理 - manageJob()小结 概述 源码版本:kubern ...

  5. [Spark内核] 第32课:Spark Worker原理和源码剖析解密:Worker工作流程图、Worker启动Driver源码解密、Worker启动Executor源码解密等

    本課主題 Spark Worker 原理 Worker 启动 Driver 源码鉴赏 Worker 启动 Executor 源码鉴赏 Worker 与 Master 的交互关系 [引言部份:你希望读者 ...

  6. [Spark內核] 第41课:Checkpoint彻底解密:Checkpoint的运行原理和源码实现彻底详解

    本课主题 Checkpoint 运行原理图 Checkpoint 源码解析 引言 Checkpoint 到底是什么和需要用 Checkpoint 解决什么问题: Spark 在生产环境下经常会面临 T ...

  7. Dubbo原理和源码解析之服务引用

    一.框架设计 在官方<Dubbo 开发指南>框架设计部分,给出了引用服务时序图: 另外,在官方<Dubbo 用户指南>集群容错部分,给出了服务引用的各功能组件关系图: 本文将根 ...

  8. Dubbo原理和源码解析之标签解析

    一.Dubbo 配置方式 Dubbo 支持多种配置方式: XML 配置:基于 Spring 的 Schema 和 XML 扩展机制实现 属性配置:加载 classpath 根目录下的 dubbo.pr ...

  9. Dubbo原理和源码解析之“微内核+插件”机制

    github新增仓库 "dubbo-read"(点此查看),集合所有<Dubbo原理和源码解析>系列文章,后续将继续补充该系列,同时将针对Dubbo所做的功能扩展也进行 ...

随机推荐

  1. JavaScript学习笔记----- 继承的实现及其原理

    按照自己在极客上学习的顺序整理了一下,参考了几位前辈的随笔,十分感谢:                       参见http://blog.yemou.net/article/query/info ...

  2. redis 高级学习和应用场景

    redis 高级学习 1.redis 复制 2.redis 集群 3.哨兵机制 4.spring 与哨兵结合 5.数据恢复与转移 6.redis 的阻塞分析 redis 实战 1. 数据缓存(热点数据 ...

  3. nacos集群配置

    一.    环境准备 Nacos 依赖 java环境来运行.如果您是从代码开始构建并运行Nacos,还需要为此配置 Maven环境,请确保是在以下版本环境中安装使用: 64 bit OS,支持 Lin ...

  4. SWD学习笔记

    SWD其实和JTAG类似,是一种调试串口. JTAG大致了解了一下.JTAG(Joint Test Action Group)主要4 lines:TMS(模式选择),TCK(时钟),TDI(数据输入) ...

  5. Qt 调用本地浏览器打开URL

    点击Qt某些控件,查找本地浏览器打开前端传递的URL. 方法一:直接写死本地浏览器地址 QString programAdress = "C:\Program Files (x86)\Goo ...

  6. vue中配置sass(包含vue-cli 3)

    目录 vue vue cli 3 老版本的脚手架搭建的项目 版本 安装 不用修改任何配置 vue文件中使用 vue 更新时间: 2018-09-21 vue cli 3 选择 Manually sel ...

  7. ImageSwitcher和GridView的案例开发

    (一)ImageSwitcher之手机相册的滑动查看 首先在布局文件上加一个ImageSwitcher,设置它的宽度和高度为match_parent. 在主程序中:首先设置一个存储照片资源的数组,在设 ...

  8. 零基础学完Python的7大就业方向,哪个赚钱多?

    “ 我想学 Python,但是学完 Python 后都能干啥 ?” “ 现在学 Python,哪个方向最简单?哪个方向最吃香 ?” “ …… ” 相信不少 Python 的初学者,都会遇到上面的这些问 ...

  9. 浅谈PHP组件、框架以及Composer

    本篇文章主要介绍了PHP组件.框架以及Composer,具有一定的学习价值,感兴趣的朋友可以了解一下. 什么是组件 组件是一组打包的代码,是一系列相关的类.接口和Trait,用于帮助我们解决PHP应用 ...

  10. 1. 使用 Docker 安装 Kong

    Create a Docker network $ docker network create kong-net Start Database $ docker run -d --name kong- ...