sparkStreaming算子之updateStateByKey

updateStateByKey操作允许我们维护任意状态,同时不断地用新信息更新它。
在有新的数据信息进入或更新时,可以让用户保持想要的任何状态。使用这个功能需要完成两步:

  1. 定义状态: 可以是任意数据类型
  2. 定义状态更新函数: 用一个函数指定如何使用先前的状态,从输入流中的新值更新状态。

对于有状态操作,要不断的把当前和历史的时间切片的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();
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值