前言

当我在测试SparkStreaming的状态操作mapWithState算子时,当我们设置timeout(3s)的时候,3s过后数据还是不会过期,不对此key进行操作,等到30s左右才会清除过期的数据。

百度了很久,关于timeout的资料很少,更没有解决这个问题的文章,所以说,百度也不是万能的,有时候还是需要靠自己。

所以我就在周末研究了一下,然后将结果整理了出来,希望能帮助大家更全面的理解Spark状态计算。

mapWithState

按理说Spark Streaming实时处理,数据就像流水,每个批次之间的数据都是独立的,处理完就处理完了,不留下任何状态。但是免不了一些有状态的操作,例如统计从流启动到现在,某个单词出现了多少次,所以状态操作就出现了。

状态操作分为updateStateByKey和mapWithState,两者有着很大的区别。简单的来说,前者每次输出的都是全量状态,后者输出的是增量状态。

过期原理

过期这一块估计很多人开始都理解错了,我刚开始理解就是数据从出现,经过多少秒之后就会过期。其实不是,这里的过期指的是空闲时间。

注释大概是这个意思:timeout()传入一个时间间隔参数,如果一个key在大于此间隔没有此key的数据流入,则被认为是空闲的,就会单独调用一次mapWithState中的func来清除这些空闲数据状态。

先写结论

使用了timeout()之后,需要使用以下代码来在间隔内清除失效key。

stream.checkpoint(Seconds(6))

checkpoint的时候,会开启全面扫描,才会对state中的失效key进行清理。

测试

   val conf = new SparkConf().setMaster("local[2]").setAppName("state")
val ssc = new StreamingContext(conf, Seconds(3))
ssc.checkpoint("./tmp")
val streams: DStream[(String, Int)] = ssc.socketTextStream("localhost", 9999)
.map(x => (x, 1)) val result = streams.mapWithState(StateSpec.function((k: String, v: Option[Int], state: State[Int]) => {
val count = state.getOption().getOrElse(0)
println(k)
println(v)
var sum = 0
if (!state.isTimingOut()) {
sum = count + v.get
state.update(sum)
} else {
println("timeout")
}
Option(sum)
})
.timeout(Seconds(3))
)
// 这行代码是触发清除机制的关键
// result.checkpoint(Seconds(6))
result.print()
ssc.start()
ssc.awaitTermination()

使用上面的代码进行测试,设置过期时间为3s。但是3s过后发现key并没有过期,也不会被清除,大概30S之后被清除。

在9999端口输入一个tom后,不再进行任何操作。测试结果如下:

tom
Some(1)
-------------------------------------------
Time: 1618228587000 ms
-------------------------------------------
Some(1) tom
None
timeout
-------------------------------------------
Time: 1618228614000 ms
-------------------------------------------
Some(0)

从测试结果可以看出,从输入到清除大概是27s。

我们现在将注释的代码放开,每6s进行checkpoint一次,输入tom:

tom
Some(1)
-------------------------------------------
Time: 1618228497000 ms
-------------------------------------------
Some(1) tom
None
timeout
-------------------------------------------
Time: 1618228506000 ms
-------------------------------------------
Some(0)

从生成到清除用了9秒,正好是过期时间 + 下一个窗口时间,触发了checkpoint。

猜想

第一次学状态操作的时候,就考虑如何去掉一些过期的key,通过timeout()的方法没有完成自己想法,从网上也没有找到解决方案,所以就暂且搁置在一边了。后来又回过头来考虑这个问题,然后根据自己的想法去猜想、去验证。

1. 我先看的是mapWithState()的返回值

2. MapWithStateDStreamImpl

每个Dstream的计算逻辑都在compute()中,这里是调用了internalStream的getOrCompute(),根据继承关系,调用的是父类Dstream的此方法:

getOrCompute()主要功能为:计算、缓存、checkpoint。这里只需要记住几个地方:checkpointDuration,即checkpoint间隔,和调用了checkpoint()。其实真正的计算还是调用了compute(),接着去看compute()

3. InternalMapWithStateDStream

compute()里面也调用了getOrCompute()方法,其实和上面调用的一样,都是Dstream的,这里主要看的是使用createFromRDD()生成的StateRDD。

4. MapWithStateRDD

这个StateRDD就是参与状态计算的数据集合,首先看它是如何生成的:

再看看StateRDD的compute()是如何计算的:

从compute()看出,当doFullScan为true的时候,才会触发过期key的清除,updateRecordWithData()负责全面扫描清除过期key

这不,思路就来了,我们只要找到开启FullScan的方法,不就可以自行触发清除机制了吗!

那么,我们先看看doFullScan的默认值:

默认是没开启的,接着通过快捷键看看哪些地方使用了doFullScan:

从图中看出,有两处代码修改了doFullScan,我们找到这两处代码:



第一个基本上排除,那么就剩下第二个:checkpoint(),我们要知道的是,状态操作必须要checkpoint

还记得在2中的getOrCompute()吗,当checkpointDuration不为null的时候,调用checkpoint()。

我们来看3中InternalMapWithStateDStream是如何定义这个duration的:



如图,sideDuration是窗口时间,乘以系数10就是默认的checkpoint时长,所以当我设置窗口为3s时,checkpoint周期就是30s,30s才会清理一次过期key。

而通过checkpoint(interval)可以设置checkpoint的间隔,所以覆盖了上面程序中默认的30s。

5.MapWithStateRDDRecord

最后提一提,FullScan是在这个类中开启的,所以先看看这个Record的注释介绍:

意思就是负责存储StateRDD的状态KV,updateRecordWithData()负责清除过期的Record,我们来看看这个方法的实现:

removeTimedoutData就是是否开启全面扫描,即doFullScan的值。

结语

写完看起来感觉真的是简简单单,逻辑看起来也比较清晰,但是自己去解决这个问题的时候也是花了一下午时间,过期key的清除与checkpoint有关也是我凭空弄猜想,然后分析了两次,某一瞬间才找到他们之间的关系。所以说,猜想和运气还是很重要的。

当然,找不到关于这块的文章和资料可能是因为这个知识点太小了。所以这次过后,要开始系统阅读Spark源码了,也希望在某一天能结合着自己的理解,写一下Spark的文章。


95后小程序员,写的都是日常工作中的亲身实践,置身于初学者的角度从0写到1,详细且认真。

文章会在公众号 [入门到放弃之路] 首发,期待你的关注。



SparkStreaming使用mapWithState时,设置timeout()无法生效问题解决方案的更多相关文章

  1. 测试setsockopt设置超时是否生效代码

    // 测试setsockopt设置超时是否生效代码 #include <arpa/inet.h> #include <netinet/in.h> #include <st ...

  2. HTTP请求的python实现(urlopen、headers处理、 Cookie处理、设置Timeout超时、 重定向、Proxy的设置)

    python实现HTTP请求的三中方式:urllib2/urllib.httplib/urllib 以及Requests urllib2/urllib实现 urllib2和urllib是python两 ...

  3. listview当选中某一个item时设置背景色其他的不变

    listview当选中某一个item时设置背景色其他的不变: 可以使用listview.setOnFoucsChangeListener(listener) ; /** * listview获得焦点和 ...

  4. [转]Loadrunner11之VuGen运行时设置Run-Time Setting

    转自:http://www.51testing.com/html/92/450992-248065.html General 1.Run Logic运行逻辑 脚本如何运行的,每个action和acti ...

  5. LoadRunner 学习笔记(2)VuGen运行时设置Run-Time Setting

    定义:在Vugen中Run-Time Setting是用来设置脚本运行时所需要的相关选项

  6. input 类型为number型时,maxlength不生效?

    input 类型为number型时,maxlength不生效? 可以加oninput属性来控制最大长度:<input id="numInput" type="num ...

  7. AForge调用摄像头拍照时设置分辨率

    简单记录下AForge2.2.5.0版本调用摄像头拍照时设置分辨率的方法. FilterInfo info = _videoDevices[0];//获取第一个摄像头 _cameraDevice = ...

  8. c/c++ linux epoll系列3 利用epoll_wait设置timeout时间长度

    linux epoll系列3 利用epoll_wait设置timeout时间长度 epoll_wait函数的第四个参数可以设置,epoll_wait函数的等待时间(timeout时间长度). 例子1, ...

  9. loadrunner 运行场景-运行时设置

    运行场景-运行时设置 by:授客 QQ:1033553122 A.   查看.修改单个脚本的运行时设置 a)   途径1: Scenario Groups.Scenario Groups Script ...

随机推荐

  1. js & bitwise operator

    js & bitwise operator bitwise operator https://github.com/Advanced-Frontend/Daily-Interview-Ques ...

  2. 正则表达式匹配${key}并在Java中使用

    1.正则表达式匹配${key} \$\{([a-z]+)\} 能够匹配字符串中以${key}形式的文本(其中key为小写应为字母) .*\$\{([a-z]+)\}.* 可以用来检测文本中是否有${k ...

  3. 解决QQ能正常上网但是网页无法打开的办法

    最近网页老师稀里糊涂的打不开,在这里附上参考的行之有效的办法. https://baijiahao.baidu.com/s?id=1645363213803553998&wfr=spider& ...

  4. Prism -- 简介

    Prism是一个开源框架,用于在WPF.Xamarin Forms.Uno/Win UI等应用中创建松耦合.可维护.可测试的XAML应用程序.Prism提供了一组设计模式的实现,这些设计模式有助于编写 ...

  5. 下载HLS视频到本地

    现在绝大多数网站播放视频都采用HLS技术,像腾讯优酷爱奇艺等等.本篇博文将介绍如何下载这样的视频到本地. 前言 因疫情影响,上课部分课程采用腾讯课堂上课,腾讯课堂有直播回放功能,但这个功能腾讯显然没有 ...

  6. Loki日志系统

    一.概述 背景 Loki的第一个稳定版本于2019年11月19日发布,是 Grafana Labs 团队最新的开源项目,是一个水平可扩展,高可用性,多租户的日志聚合系统. Grafana 对 Loki ...

  7. Python切换版本工具pyenv

    目录 安装pyenv 安装与查看py版本 切换py版本 结合ide使用示例 和virtualenv的一些区别 参考文献 使用了一段时间,我发现这玩意根本不是什么神器,简直就是垃圾,安装多版本总是失败, ...

  8. pytorch(08)数据模型的读取(2)

    import numpy as np import torch import os import random from PIL import Image from torch.utils.data ...

  9. salesforce零基础学习(一百零一)如何了解你的代码得运行上下文

    本篇参考:https://developer.salesforce.com/docs/atlas.en-us.228.0.apexcode.meta/apexcode/apex_enum_System ...

  10. 线上MySQL读写分离,出现写完读不到问题如何解决

    大家好,我是历小冰. 今天我们来详细了解一下主从同步延迟时读写分离发生写后读不到的问题,依次讲解问题出现的原因,解决策略以及 Sharding-jdbc.MyCat 和 MaxScale 等开源数据库 ...