关于mapWithState
需要自己写一个匿名函数func来实现自己想要的功能。如果有初始化的值得需要,可以使用initialState(RDD)来初始化key的值。 另外,还可以指定timeout函数,该函数的作用是,如果一个key超过timeout设定的时间没有更新值,那么这个key将会失效。这个控制需要在func中实现,必须使用state.isTimingOut()来判断失效的key值。如果在失效时间之后,这个key又有新的值了,则会重新计算。如果没有使用isTimingOut,则会报错。
注意事项
下面程序是使用idea编写的,使用的是scala语言,在程序中master(“local[2]”)设置为本地模式([]中的数指定的是线程数,不能少于2,否则看不到结果。主要是因为spark需要启动一个线程receiver来循环接收数据,一个Executor来接收数据,如果少于2线程不够将不能打印出结果。),在window上运行的。使用的spark版本是2.3.0,在2.x以后的版本,基本采用SparkSession来进行操作。同时,想要运行程序你的服务器上还必须要安装netcat这个软件,使用yum install nc进行安装(注意安全配置好yum源,DNS才能下载安装),使用命令nc -lk 6666开启服务发送数据。最后在运行程序前还需要导入spark、scala相应的依赖包。
示例代码
package spark2x
import org.apache.spark.sql.SparkSession
import org.apache.spark.streaming.dstream.{DStream, MapWithStateDStream, ReceiverInputDStream}
import org.apache.spark.streaming.{Seconds, State, StateSpec, StreamingContext}
/**
* 类名 MapWithState
* 作者 彭三青
* 创建时间 2018-12-01 14:08
* 版本 1.0
* 描述: $
*/
object MapWithState {
// 设置本地运行模式
def main(args: Array[String]): Unit = {
val spark = SparkSession.builder()
.master("local[2]")
.appName("MapWithState")
.getOrCreate()
// 创建一个context,批次间隔为2秒钟,
val ssc: StreamingContext = new StreamingContext(spark.sparkContext, Seconds(3))
// 设置checkpoint目录
ssc.checkpoint("hdfs://SC01:8020/user/tmp/cp-20181201-2")
// 创建一个ReceiverInputDStream,从服务器端的netcat接收数据。
// 服务器主机名SC01(SC01已在Window上的hosts文件中做了映射,没做映射的则写ip就OK了),监听端口为6666
val line: ReceiverInputDStream[String] = ssc.socketTextStream("SC01", 6666)
// 对接收到的数据进行处理,进行切割,分组形式为(day, 1) (word 1)
val wordsStream: DStream[(String, Int)] = line.flatMap(_.split(" ")).map((_, 1))
val wordCount: MapWithStateDStream[String, Int, Int, Any] = wordsStream.mapWithState(StateSpec.function(func).timeout(Seconds(30)))
wordCount.print()
ssc.start()
ssc.awaitTermination()
}
/**
* 定义一个函数,该函数有三个类型word: String, option: Option[Int], state: State[Int]
* 其中word代表统计的单词,option代表的是历史数据(使用option是因为历史数据可能有,也可能没有,如第一次进来的数据就没有历史记录),state代表的是返回的状态
*/
val func = (word: String, option: Option[Int], state: State[Int]) => {
if(state.isTimingOut()){
println(word + "is timeout")
}else{
// getOrElse(0)不存在赋初始值为零
val sum = option.getOrElse(0) + state.getOption().getOrElse(0)
// 单词和该单词出现的频率/ 获取历史数据,当前值加上上一个批次的该状态的值
val wordFreq = (word, sum)
// 更新状态
state.update(sum)
wordFreq
}
}
}
运行
服务器运行nc
idea端运行编写好的程序
服务器发送数据
控制台显示结果
结论
mapWithState它会按照时间线在每一个批次间隔返回之前的发生改变的或者新的key的状态,不发生变化的不返回;同时mapWithState可以不用设置checkpoint,返回的数据量少。而updateStateByKey统计的是全局Key的状态,就算没有数据输入也会在每个批次的时候返回之前的Key的状态,当数据量大时而且使用checkpoint会占用较大的存储。因此,mapWithState性能和效率要比updateStateByKey好。
第一篇:Spark Streaming状态管理函数(一)——updateStateByKey和mapWithState
第二篇:Spark Streaming状态管理函数(二)——updateStateByKey的使用(scala版)
更新时间
2018-12-10 更换了图片错误,改变了代码块的风格
2019-09-23 修改并增加了文章末尾的内容
2019-10-31 代码部分粘贴错误更新
## 最后
本人想组建一个技术交流群,有兴趣的可以扫码加入,用于学习和交流,希望和大家一起学习一起进步。
QQ群:814507419
微信群:由于微信群二维码会失效,大家可以加我微信,然后拉大家进群期待和大家一起进步一起学习。微信号18397716181