Spark Streaming中的有状态计算

Spark Streaming中数据是以微批的形式进来的,如果要基于之前的批处理的处理结果进行计算的话,就必须对之前计算的结果进行管理。可以通过外部存储实现,也可以通过Spark Streaming提供的状态管理来实现。

Spark Streaming提供了两个算子来实现流状态管理:一个是updateStateByKey,另一个是mapWithState。

updateStateByKey

updateStateByKey允许维护任意的状态,并通过新进来的数据更新状态。

在数据流中,通过将给定函数应用于键的前一个状态和该键的新值来更新每个键的状态。

示例

我们以WordCount为例,要统计所有批次总的WordCount,就可以通过updateStateByKey来实现。

由于是测试,我使用的是socketTextStream接受数据,通过netcat来发送数据。

通过nc -l -p 端口号来创建某个端口的连接,当我们的流程序跑起来后,就可以通过命令行发送word到spark程序。

val sparkConf = new SparkConf()
    .setMaster("local[2]")
    .setAppName("updateByKeyDemo")

val sc = new SparkContext(sparkConf)
sc.setLogLevel("WARN")

val ssc = new StreamingContext(sc, Seconds(5))
// 有状态计算必须要指定checkpoint目录
ssc.checkpoint("file:///D:/checkpoint/")

// 监听本地的1234端口
val stream = ssc.socketTextStream("localhost", 1234)
// 对输入进行切换,然后转换为(word, 1)的元组
val wordPairStream = stream
    .flatMap(_.split(" "))
    .map((_, 1))

// 执行有状态的计算,并输出
wordPairStream.updateStateByKey(updateFunc _)
     .print()

ssc.start()
ssc.awaitTermination()

// ---------------------------------------------------
// 定义更新函数 
def updateFunc(newValues: Seq[Int], runningCount: Option[Int]): Option[Int] = {
  var count = runningCount.getOrElse(0)
  for (value <- newValues) {
      count += value
  }
  Some(count)
}
说明

由以上demo可知,将自定义的状态更新函数作为参数传给updateStateByKey算子即可实现状态计算。

接下来看一下updateStateByKey算子的一些参数:

参数名说明
updateFunc状态更新函数。根据“之前批次的状态值“和”当前批次的值“来更新状态值
numPartitions新的Dstream中每个RDD的分区数。
partitioner指定分区器
rememberPartitioner是否记住生成的RDD中的分区对象
initialRDD每个key的初始状态值对应的RDD

根据参数的不同,updateStateByKey有7个重载函数,但都是大同小异,主要说一下updateFunc。粗略有以下两种updateFunc

  • updateFunc: (Seq[V], Option[S]) => Option[S]

参数:Seq[V]代表本批次K对应的Values,Option[S]代表之前批次的状态值

返回值:Option[S]表示当前批次计算后的状态值

// wordcount demo
def updateFunc(newValues: Seq[Int], runningCount: Option[Int]): Option[Int] = {
    var count = runningCount.getOrElse(0)
    for (value <- newValues) {
        count += value
    }
    Some(count)
}
  • updateFunc: (Iterator[(K, Seq[V], Option[S])]) => Iterator[(K, S)]

参数:iterator是(K, Seq[V], Option[S])类型的Iterator,K,是当前key,Seq[V]是本批次K对应的Values,Option[S]是历史状态值

返回值:Iterator[(K, S)]是返回的新状态值

// wordcount demo
def updateFunc2(iterator: Iterator[(String, Seq[Int], Option[Int])]): Iterator[(String, Int)] = {
    iterator.map(it => {
        var count = it._3.getOrElse(0)
        for (v <- it._2) {
            count += v
        }
        (it._1, count)
    })
}

mapWithState

mapWithState和updateStateByKey类似,也可以实现key的状态管理。

实例

也是一个wordcount demo,和上面实现的是一样的功能

val sparkConf = new SparkConf()
    .setMaster("local[2]")
    .setAppName("updateByKeyDemo")

val sc = new SparkContext(sparkConf)
sc.setLogLevel("WARN")

val ssc = new StreamingContext(sc, Seconds(5))
ssc.checkpoint("file:///D:/checkpoint/")

val stream = ssc.socketTextStream("localhost", 1234)
val wordPairStream = stream
    .flatMap(_.split(" "))
    .map((_, 1))

// 使用mapWithState算子来实现状态管理
wordPairStream
    .mapWithState(
        StateSpec.function(mappingFunc _)
    )
    .print()

ssc.start()
ssc.awaitTermination()

// ------------------------------------------------
// 自定义的状态更新函数
def mappingFunc(word: String, value: Option[Int], state: State[Int]): (String, Int) = {
    val count = value.getOrElse(0) + state.getOption().getOrElse(0)
    state.update(count)
    (word, count)
}

说明

将我们自定义的函数包装成StateSpec作为mapWithState的参数来实现状态计算。

说一说我们自定义的函数,相关的信息如下:

mappingFunction: (KeyType, Option[ValueType], State[StateType]) => MappedType

参数:KeyType代表key,Option[ValueType]代表当前批次的值,State[StateType]表示状态值

返回值:MappedType代表返回值,就是我们要输出的值.

// wprdcpunt demo
def mappingFunc(word: String, value: Option[Int], state: State[Int]): (String, Int) = {
    val count = value.getOrElse(0) + state.getOption().getOrElse(0)
    state.update(count)
    (word, count)
}

总结

updateStateByKey和mapWithState都可以实现基于状态的计算,都需要设置checkpoint。

updateStateByKey不管状态是否更新,都会输出所有的状态值;mapWithState只会输出状态有更新的状态值,但是也会保留所有的状态值。

理论上是mapWithState性能更好,但是还是要看具体的场景。

更多相关内容可阅读下面的参考链接。

参考

Spark UpdateStateByKey官方文档

mapWithState 官方demo

updateStateByKey各个重载函数的说明

Spark Streaming状态管理函数

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值