14:Spark Streaming源码解读之State管理之updateStateByKey和mapWithState解密
首先简单解释一下什么是state(状态)管理?我们以wordcount为例。每个batchInterval会计算当前batch的单词计数,那如果需要计算从流开始到目前为止的单词出现的次数,该如计算呢?SparkStreaming提供了两种方法:updateStateByKey和mapWithState 。mapWithState 是1.6版本新增功能,目前属于实验阶段。mapWithState具官方说性能较updateStateByKey提升10倍。那么我们来看看他们到底是如何实现的。
object UpdateStateByKeyDemo {def main(args: Array[String]) {val conf = new SparkConf().setAppName("UpdateStateByKeyDemo")val ssc = new StreamingContext(conf,Seconds(20))//要使用updateStateByKey方法,必须设置Checkpoint。ssc.checkpoint("/checkpoint/")val socketLines = ssc.socketTextStream("localhost",9999)socketLines.flatMap(_.split(",")).map(word=>(word,1)).updateStateByKey((currValues:Seq[Int],preValue:Option[Int]) =>{val currValue = currValues.sum //将目前值相加Some(currValue + preValue.getOrElse(0)) //目前值的和加上历史值}).print()ssc.start()ssc.awaitTermination()ssc.stop()}}
implicit def toPairDStreamFunctions[K, V](stream: DStream[(K, V)])(implicit kt: ClassTag[K], vt: ClassTag[V], ord: Ordering[K] = null):PairDStreamFunctions[K, V] = {new PairDStreamFunctions[K, V](stream)}
def updateStateByKey[S: ClassTag](updateFunc: (Seq[V], Option[S]) => Option[S]): DStream[(K, S)] = ssc.withScope {updateStateByKey(updateFunc, defaultPartitioner())}
def updateStateByKey[S: ClassTag](updateFunc: (Iterator[(K, Seq[V], Option[S])]) => Iterator[(K, S)],partitioner: Partitioner,rememberPartitioner: Boolean): DStream[(K, S)] = ssc.withScope {new StateDStream(self, ssc.sc.clean(updateFunc), partitioner, rememberPartitioner, None)}
private [this] def computeUsingPreviousRDD (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 functionval updateFuncLocal = updateFuncval finalFunc = (iterator: Iterator[(K, (Iterable[V], Iterable[S]))]) => {val i = iterator.map { t =>val itr = t._2._2.iteratorval headOption = if (itr.hasNext) Some(itr.next()) else None(t._1, t._2._1.toSeq, headOption)}updateFuncLocal(i)}val cogroupedRDD = parentRDD.cogroup(prevStateRDD, partitioner)val stateRDD = cogroupedRDD.mapPartitions(finalFunc, preservePartitioning)Some(stateRDD)}
object StatefulNetworkWordCount {def main(args: Array[String]) {if (args.length < 2) {System.err.println("Usage: StatefulNetworkWordCount <hostname> <port>")System.exit(1)}StreamingExamples.setStreamingLogLevels()val sparkConf = new SparkConf().setAppName("StatefulNetworkWordCount")// Create the context with a 1 second batch sizeval ssc = new StreamingContext(sparkConf, Seconds(1))ssc.checkpoint(".")// Initial state RDD for mapWithState operationval initialRDD = ssc.sparkContext.parallelize(List(("hello", 1), ("world", 1)))// Create a ReceiverInputDStream on target ip:port and count the// words in input stream of \n delimited test (eg. generated by 'nc')val lines = ssc.socketTextStream(args(0), args(1).toInt)val words = lines.flatMap(_.split(" "))val wordDstream = words.map(x => (x, 1))// Update the cumulative count using mapWithState// This will give a DStream made of state (which is the cumulative count of the words)val mappingFunc = (word: String, one: Option[Int], state: State[Int]) => {val sum = one.getOrElse(0) + state.getOption.getOrElse(0)val output = (word, sum)state.update(sum)output}val stateDstream = wordDstream.mapWithState(StateSpec.function(mappingFunc).initialState(initialRDD))stateDstream.print()ssc.start()ssc.awaitTermination()}}
mapWithState接收的参数是一个StateSpec对象。在StateSpec中封装了状态管理的函数
mapWithState函数中创建了MapWithStateDStreamImpl对象
def mapWithState[StateType: ClassTag, MappedType: ClassTag](spec: StateSpec[K, V, StateType, MappedType]): MapWithStateDStream[K, V, StateType, MappedType] = {new MapWithStateDStreamImpl[K, V, StateType, MappedType](self,spec.asInstanceOf[StateSpecImpl[K, V, StateType, MappedType]])}
/** Internal implementation of the [[MapWithStateDStream]] */private[streaming] class MapWithStateDStreamImpl[KeyType: ClassTag, ValueType: ClassTag, StateType: ClassTag, MappedType: ClassTag](dataStream: DStream[(KeyType, ValueType)],spec: StateSpecImpl[KeyType, ValueType, StateType, MappedType])extends MapWithStateDStream[KeyType, ValueType, StateType, MappedType](dataStream.context) {private val internalStream =new InternalMapWithStateDStream[KeyType, ValueType, StateType, MappedType](dataStream, spec)override def slideDuration: Duration = internalStream.slideDurationoverride def dependencies: List[DStream[_]] = List(internalStream)override def compute(validTime: Time): Option[RDD[MappedType]] = {internalStream.getOrCompute(validTime).map { _.flatMap[MappedType] { _.mappedData } }}
/** Method that generates a RDD for the given time */override def compute(validTime: Time): Option[RDD[MapWithStateRDDRecord[K, S, E]]] = {// Get the previous state or create a new empty state RDDval prevStateRDD = getOrCompute(validTime - slideDuration) match {case Some(rdd) =>if (rdd.partitioner != Some(partitioner)) {// If the RDD is not partitioned the right way, let us repartition it using the// partition index as the key. This is to ensure that state RDD is always partitioned// before creating another state RDD using itMapWithStateRDD.createFromRDD[K, V, S, E](rdd.flatMap { _.stateMap.getAll() }, partitioner, validTime)} else {rdd}case None =>MapWithStateRDD.createFromPairRDD[K, V, S, E](spec.getInitialStateRDD().getOrElse(new EmptyRDD[(K, S)](ssc.sparkContext)),partitioner,validTime)}// Compute the new state RDD with previous state RDD and partitioned data RDD// Even if there is no data RDD, use an empty one to create a new state RDDval dataRDD = parent.getOrCompute(validTime).getOrElse {context.sparkContext.emptyRDD[(K, V)]}val partitionedDataRDD = dataRDD.partitionBy(partitioner)val timeoutThresholdTime = spec.getTimeoutInterval().map { interval =>(validTime - interval).milliseconds}Some(new MapWithStateRDD(prevStateRDD, partitionedDataRDD, mappingFunction, validTime, timeoutThresholdTime))}
override def compute(partition: Partition, context: TaskContext): Iterator[MapWithStateRDDRecord[K, S, E]] = {val stateRDDPartition = partition.asInstanceOf[MapWithStateRDDPartition]val prevStateRDDIterator = prevStateRDD.iterator(stateRDDPartition.previousSessionRDDPartition, context)val dataIterator = partitionedDataRDD.iterator(stateRDDPartition.partitionedDataRDDPartition, context)- //prevRecord 代表一个分区的数据
val prevRecord = if (prevStateRDDIterator.hasNext) Some(prevStateRDDIterator.next()) else Noneval newRecord = MapWithStateRDDRecord.updateRecordWithData(prevRecord,dataIterator,mappingFunction,batchTime,timeoutThresholdTime,removeTimedoutData = doFullScan // remove timedout data only when full scan is enabled)Iterator(newRecord)}
private[streaming] case class MapWithStateRDDRecord[K, S, E](var stateMap: StateMap[K, S], var mappedData: Seq[E])
def updateRecordWithData[K: ClassTag, V: ClassTag, S: ClassTag, E: ClassTag](prevRecord: Option[MapWithStateRDDRecord[K, S, E]],dataIterator: Iterator[(K, V)],mappingFunction: (Time, K, Option[V], State[S]) => Option[E],batchTime: Time,timeoutThresholdTime: Option[Long],removeTimedoutData: Boolean): MapWithStateRDDRecord[K, S, E] = {// 创建一个新的 state map 从过去的Recoord中复制 (如果存在) 否则创建一下空的StateMap对象val newStateMap = prevRecord.map { _.stateMap.copy() }. getOrElse { new EmptyStateMap[K, S]() }val mappedData = new ArrayBuffer[E]- //状态
val wrappedState = new StateImpl[S]()// Call the mapping function on each record in the data iterator, and accordingly// update the states touched, and collect the data returned by the mapping functiondataIterator.foreach { case (key, value) =>//获取key对应的状态wrappedState.wrap(newStateMap.get(key))- //调用mappingFunction获取返回值
val returned = mappingFunction(batchTime, key, Some(value), wrappedState)//维护newStateMap的值if (wrappedState.isRemoved) {newStateMap.remove(key)} else if (wrappedState.isUpdated|| (wrappedState.exists && timeoutThresholdTime.isDefined)) {newStateMap.put(key, wrappedState.get(), batchTime.milliseconds)}mappedData ++= returned}// Get the timed out state records, call the mapping function on each and collect the// data returnedif (removeTimedoutData && timeoutThresholdTime.isDefined) {newStateMap.getByTime(timeoutThresholdTime.get).foreach { case (key, state, _) =>wrappedState.wrapTimingOutState(state)val returned = mappingFunction(batchTime, key, None, wrappedState)mappedData ++= returnednewStateMap.remove(key)}}MapWithStateRDDRecord(newStateMap, mappedData)}
14:Spark Streaming源码解读之State管理之updateStateByKey和mapWithState解密的更多相关文章
- Spark Streaming源码解读之State管理之UpdataStateByKey和MapWithState解密
本期内容 : UpdateStateByKey解密 MapWithState解密 Spark Streaming是实现State状态管理因素: 01. Spark Streaming是按照整个Bach ...
- Spark Streaming源码解读之JobScheduler内幕实现和深度思考
本期内容 : JobScheduler内幕实现 JobScheduler深度思考 JobScheduler 是整个Spark Streaming调度的核心,需要设置多线程,一条用于接收数据不断的循环, ...
- Spark Streaming源码解读之流数据不断接收和全生命周期彻底研究和思考
本节的主要内容: 一.数据接受架构和设计模式 二.接受数据的源码解读 Spark Streaming不断持续的接收数据,具有Receiver的Spark 应用程序的考虑. Receiver和Drive ...
- 15、Spark Streaming源码解读之No Receivers彻底思考
在前几期文章里讲了带Receiver的Spark Streaming 应用的相关源码解读,但是现在开发Spark Streaming的应用越来越多的采用No Receivers(Direct Appr ...
- Spark Streaming源码解读之流数据不断接收全生命周期彻底研究和思考
本期内容 : 数据接收架构设计模式 数据接收源码彻底研究 一.Spark Streaming数据接收设计模式 Spark Streaming接收数据也相似MVC架构: 1. Mode相当于Rece ...
- Spark Streaming源码解读之Receiver生成全生命周期彻底研究和思考
本期内容 : Receiver启动的方式设想 Receiver启动源码彻底分析 多个输入源输入启动,Receiver启动失败,只要我们的集群存在就希望Receiver启动成功,运行过程中基于每个Tea ...
- Spark Streaming源码解读之生成全生命周期彻底研究与思考
本期内容 : DStream与RDD关系彻底研究 Streaming中RDD的生成彻底研究 问题的提出 : 1. RDD是怎么生成的,依靠什么生成 2.执行时是否与Spark Core上的RDD执行有 ...
- Spark Streaming源码解读之Job动态生成和深度思考
本期内容 : Spark Streaming Job生成深度思考 Spark Streaming Job生成源码解析 Spark Core中的Job就是一个运行的作业,就是具体做的某一件事,这里的JO ...
- 16.Spark Streaming源码解读之数据清理机制解析
原创文章,转载请注明:转载自 听风居士博客(http://www.cnblogs.com/zhouyf/) 本期内容: 一.Spark Streaming 数据清理总览 二.Spark Streami ...
随机推荐
- 题解【bzoj2440 [中山市选2011]完全平方数】
Description 求第 \(k\) 个不含平方因子的正整数.多组询问.\(k \leq 10^9, T \leq 50\) Solution 网上的题解几乎都是容斥,这里给一个简单的也挺快的做法 ...
- 8.Android_UiAutomator 报告查看
一.Android UiAutomator报告查看 1.错误类型 1)断言错误:就是断言这个用例的成功或者失败(AssrtionFailedError) 2)脚本错误:UiObjectNotFound ...
- is_int is_float 判断数值是否为整数
is_float — 检测变量是否是浮点型 http://cn.php.net/manual/zh/function.is-float.php is_int — 检测变量是否是整数 http://cn ...
- RabbitMQ的基础介绍
转自:http://blog.csdn.net/whycold/article/details/41119807 一.引言 你是否遇到过两个(多个)系统间需要通过定时任务来同步某些数据?你是否在为异构 ...
- APP的数据采集与埋点方式分析
前言: 神策数据写过几篇分析APP前后端埋点的文章,原文在此: https://sensorsdata.cn/blog/shu-ju-jie-ru-yu-mai-dian/ http://www.wo ...
- Mybatis批量删除之Error code 1064, SQL state 42000;
(一)小小的一次记载. (二):最近的项目都是使用MyBatis,批量新增自己都会写了,但是一次批量删除可把我给折腾了下,写法网上都有,但是照着做就是不行,最后问公司的人,问网友才得到答案,那就是jd ...
- spfa+剪枝 或者 dij+手写堆+剪枝 UOJ 111
http://uoj.ac/problem/111 好像NOIP里面的题目...有好多都是...能通过xjbg剪枝来...AC题目的? 得好好学一下这些剪枝黑科技了... 思路:我觉得这位大佬说的很完 ...
- 区间->点,点->区间,线段树优化建图+dijstra Codeforces Round #406 (Div. 2) D
http://codeforces.com/contest/787/problem/D 题目大意:有n个点,三种有向边,这三种有向边一共加在一起有m个,然后起点是s,问,从s到所有点的最短路是多少? ...
- 天梯赛 L1-009 N个数求和 (模拟)
本题的要求很简单,就是求N个数字的和.麻烦的是,这些数字是以有理数"分子/分母"的形式给出的,你输出的和也必须是有理数的形式. 输入格式: 输入第一行给出一个正整数N(<=1 ...
- 搭建自己的PHP框架心得——转载
原文:http://www.cnblogs.com/zhenbianshu/p/5331165.html 前言 说到写PHP的MVC框架,大家想到的第一个词--“造轮子”,是的,一个还没有深厚功力的程 ...