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

Demo

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

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

源码

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

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

入口就是 updateStateByKey

PairDStreamFunctions

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

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

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

StateDStream

  1. class StateDStream[K: ClassTag, V: ClassTag, S: ClassTag](
  2. parent: DStream[(K, V)],
  3. updateFunc: (Time, Iterator[(K, Seq[V], Option[S])]) => Iterator[(K, S)],
  4. partitioner: Partitioner,
  5. preservePartitioning: Boolean,
  6. initialRDD: Option[RDD[(K, S)]]
  7. ) extends DStream[(K, S)](parent.ssc) {
  8. // 这边注意,这个StateDStream 需要设置checkpoint 地址 来保存数据。
  9. super.persist(StorageLevel.MEMORY_ONLY_SER)
  10. override val mustCheckpoint = true
  11. // 这个方法就是将 前一个batch RDD 的结果和当前计算的结果合并
  12. private [this] def computeUsingPreviousRDD(
  13. batchTime: Time,
  14. parentRDD: RDD[(K, V)],
  15. prevStateRDD: RDD[(K, S)]) = {
  16. // Define the function for the mapPartition operation on cogrouped RDD;
  17. // first map the cogrouped tuple to tuples of required type,
  18. // and then apply the update function
  19. val updateFuncLocal = updateFunc
  20. val finalFunc = (iterator: Iterator[(K, (Iterable[V], Iterable[S]))]) => {
  21. val i = iterator.map { t =>
  22. val itr = t._2._2.iterator
  23. val headOption = if (itr.hasNext) Some(itr.next()) else None
  24. (t._1, t._2._1.toSeq, headOption)
  25. }
  26. updateFuncLocal(batchTime, i)
  27. }
  28. // cogroup 合并
  29. val cogroupedRDD = parentRDD.cogroup(prevStateRDD, partitioner)
  30. // 然后将合并后的结果计算
  31. val stateRDD = cogroupedRDD.mapPartitions(finalFunc, preservePartitioning)
  32. Some(stateRDD)
  33. }
  34. override def compute(validTime: Time): Option[RDD[(K, S)]] = {
  35. // Try to get the previous state RDD
  36. // 算出上一个batch time 来获取上一个batch的RDD。
  37. getOrCompute(validTime - slideDuration) match {
  38. //如果有就说明之前有RDD,如果没有则当前是第一个batch
  39. case Some(prevStateRDD) => // If previous state RDD exists
  40. // Try to get the parent RDD
  41. // 获取当前这个批次来的数据 。这边理解有点绕,parent.getOrCompute(validTime) 就是前一个DStream 计算的结果,可以看下MappedDStream 的 方法就比较清楚了。
  42. parent.getOrCompute(validTime) match {
  43. case Some(parentRDD) => // If parent RDD exists, then compute as usual
  44. // 见两个RDD 的数据。
  45. computeUsingPreviousRDD (validTime, parentRDD, prevStateRDD)
  46. case None => // If parent RDD does not exist
  47. // Re-apply the update function to the old state RDD
  48. val updateFuncLocal = updateFunc
  49. val finalFunc = (iterator: Iterator[(K, S)]) => {
  50. val i = iterator.map(t => (t._1, Seq.empty[V], Option(t._2)))
  51. updateFuncLocal(validTime, i)
  52. }
  53. val stateRDD = prevStateRDD.mapPartitions(finalFunc, preservePartitioning)
  54. Some(stateRDD)
  55. }
  56. case None => // If previous session RDD does not exist (first input data)
  57. // Try to get the parent RDD
  58. parent.getOrCompute(validTime) match {
  59. case Some(parentRDD) => // If parent RDD exists, then compute as usual
  60. initialRDD match {
  61. case None =>
  62. // Define the function for the mapPartition operation on grouped RDD;
  63. // first map the grouped tuple to tuples of required type,
  64. // and then apply the update function
  65. val updateFuncLocal = updateFunc
  66. val finalFunc = (iterator: Iterator[(K, Iterable[V])]) => {
  67. updateFuncLocal (validTime,
  68. iterator.map (tuple => (tuple._1, tuple._2.toSeq, None)))
  69. }
  70. val groupedRDD = parentRDD.groupByKey(partitioner)
  71. val sessionRDD = groupedRDD.mapPartitions(finalFunc, preservePartitioning)
  72. // logDebug("Generating state RDD for time " + validTime + " (first)")
  73. Some (sessionRDD)
  74. case Some (initialStateRDD) =>
  75. computeUsingPreviousRDD(validTime, parentRDD, initialStateRDD)
  76. }
  77. case None => // If parent RDD does not exist, then nothing to do!
  78. // logDebug("Not generating state RDD (no previous state, no parent)")
  79. None
  80. }
  81. }
  82. }
  83. }

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. EOS主网搭建教程--&&--搭建节点--&&--搭建mongodb数据库

    EOS主网搭建教程: 1.git clone https://github.com/EOS-Mainnet/eos.git --recursive 2.cd eos 3.git tag (查看有哪些分 ...

  2. Python数据类型-3 布尔类型

    布尔类型 对于错.0和1.正与反,都是传统意义上的布尔类型. 但在Python语言中,布尔类型只有两个值,True与False.请注意,是英文单词的对与错,并且首字母要大写,不能其它花式变型. 布尔值 ...

  3. SWD学习笔记

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

  4. 「国家集训队」Crash的数字表格

    题目描述 求(对 \(20101009\) 取模,\(n,m\le10^7\) ) \[\sum_{i=1}^n\sum_{j=1}^m\operatorname{lcm}(i,j)\] 大体思路 推 ...

  5. Python语法速查: 20. 线程与并发

    返回目录 本篇索引 (1)线程基本概念 (2)threading模块 (3)线程间同步原语资源 (4)queue (1)线程基本概念 当应用程序需要并发执行多个任务时,可以使用线程.多个线程(thre ...

  6. python中提取位图信息(AttributeError: module 'struct' has no attribute 'unstack')

    前言 今天这篇博文有点意思,它是从一个例子出发,从而体现出在编程中的种种细节和一些知识点的运用.和从前一样,我是人,离成神还有几十万里,所以无可避免的出现不严谨的地方甚至错误,请酌情阅读. 0x00 ...

  7. 吴裕雄--天生自然HADOOP操作实验学习笔记:安装zookeeper集群

    实验目的 了解zookeeper的概念和原理 学会安装zookeeper集群并验证 掌握zookeeper命令使用 实验原理 1.Zookeeper介绍 ZooKeeper是一个分布式的,开放源码的分 ...

  8. 【剑指Offer面试编程题】题目1510:替换空格--九度OJ

    题目描述: 请实现一个函数,将一个字符串中的空格替换成"%20".例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy. 输入: 每个 ...

  9. 利用SSH在本机和远程服务器之间传输文件或文件夹

    1.从远程服务器上下载文件到本机 scp <服务器用户名>@<服务器地址>:<服务器中要下载的文件路径> <下载到本机的绝对路径> 2.从本机上传本地文 ...

  10. 《算法技术手册》George T. Heineman(作者)epub+mobi+azw3

    内容简介 开发健壮的软件需要高效的算法,然后程序员们往往直至问题发生之时,才会去求助于算法.<算法技术手册>讲解了许多现有的算法,可用于解决各种问题.通过阅读它,可以使您学会如何选择和实现 ...