updateStateByKey操作允许我们维护任意状态,同时不断地用新信息更新它。
在有新的数据信息进入或更新时,可以让用户保持想要的任何状态。使用这个功能需要完成两步:
- 定义状态: 可以是任意数据类型
- 定义状态更新函数: 用一个函数指定如何使用先前的状态,从输入流中的新值更新状态。
对于有状态操作,要不断的把当前和历史的时间切片的RDD累加计算,随着时间的流失,计算的数据规模会变得越来越大;
那么要思考的是如果数据量很大的时候,或者对性能的要求极为苛刻的情况下,可以考虑将数据放在Redis或者tachyon上
注意: updateStateByKey操作,要求必须开启Checkpoint机制。
?:
/**
* 状态更新函数操作
* 统计某个key,截止到目前(批次)为止的最新状态
* 最经典的例子就是来计算实时的总交易额
*/
object UpdateStateByKey {
def main(args: Array[String]): Unit = {
val conf = new SparkConf()
.setAppName("UpdateStateByKey")
.setMaster("local[*]")
val ssc = new StreamingContext(conf, Seconds(2))
//开启Checkpoint机制
ssc.checkpoint("file:///E:/data/spark/streaming/chk")
val lines:DStream[String] = ssc.socketTextStream("bigdata01", 9999)
//计算的是当前批次的数据
val ret:DStream[(String, Int)] = lines.flatMap(_.split("\\s+")).map((_, 1)).reduceByKey(_+_)
//汇总历史数据
val usb:DStream[(String, Int)] = ret.updateStateByKey(updateFunc)
usb.print()
ssc.start()
ssc.awaitTermination()
}
/**
* 状态更新函数
* @param seq
* @param option
*/
def updateFunc(seq:Seq[Int], option:Option[Int]):Option[Int] = {
Option(seq.sum + option.getOrElse(0))
}
}
因为这种带状态的操作,使用checkpoint,会产生大量的小文件,会对hdfs操作比较大的压力。
所以我们如果可以使用外置的存储系统代替的话,可以直接代替,比如在本例中使用redis代替。
/**
* 状态更新函数操作
* 统计某个key,截止到目前(批次)为止的最新状态
* 使用redis代替updateStateByKey,以及checkpoint
*/
object UpdateStateByKeyWithRedis {
def main(args: Array[String]): Unit = {
val conf = new SparkConf()
.setAppName("UpdateStateByKey")
.setMaster("local[*]")
val ssc = new StreamingContext(conf, Seconds(2))
val lines:DStream[String] = ssc.socketTextStream("bigdata01", 9999)
//计算的是当前批次的数据
val ret:DStream[(String, Long)] = lines.flatMap(_.split("\\s+")).map((_, 1L)).reduceByKey(_+_)
ret.foreachRDD((rdd, time) => {
if(!rdd.isEmpty()) {
println("-------------------------------------------")
println(s"Time: $time")
println("-------------------------------------------")
rdd.foreachPartition(partition=> {
//JedisUti在下方代码中实现了
val jedis = JedisUtil.getJedis
partition.foreach{case (word, count) => {
jedis.hincrBy("wordcount", word, count)
}}
//JedisUti在下方代码中实现了
JedisUtil.returnJedis(jedis)
})
}
})
ssc.start()
ssc.awaitTermination()
}
}
/*
java编写的JedisUtil类
*/
public class JedisUtil {
private JedisUtil(){}
private static JedisPool pool;
static {
Properties properties = new Properties();
try {
properties.load(JedisUtil.class.getClassLoader().getResourceAsStream("jedis.properties"));
String host = properties.getProperty(Constants.JEDIS_HOST);
int port = Integer.valueOf(properties.getProperty(Constants.JEDIS_PORT));
JedisPoolConfig conf = new JedisPoolConfig();
pool = new JedisPool(conf,host,port);
} catch (IOException e) {
e.printStackTrace();
}
}
public static Jedis getJedis(){
return pool.getResource();
}
public static void returnJedis(Jedis jedis){
jedis.close();
}
}