定时更新广播变量

定时更新广播变量

背景

spark streaming在处理数据的时候,需要和一些小表做join,或者从小表中获取数据,通常我们会采用广播变量的方式将数据缓存到每个worker节点上,由此,数据在exec端被计算的时候,就不需要从driver端获取小表的数据,能有效减小网络通讯开销。提高执行效率。 但是,广播变量是read-only的。 生产上,我们的这些小表是要被定时更新的。那就意味着,如果更新一次小表,就要重启一次job,重新将更新好的数据加载到内存中。显然这样是不合理的。

实现思路

1:用unpersist()方法清除缓存的数据 2:重新广播数据 3:使用计数器实现定时执行广播数据操作

代码逻辑

/**
  * Use this singleton to get or register a Broadcast variable.
  */
object DimTable {
  @volatile private var instance:  Broadcast[Array[String]] = null
​
  def getInstance(sc: SparkContext):  Broadcast[Array[String]]  = {
    if (instance == null) {
      synchronized {
        if (instance == null) {
          val dimMapTmp =  sc.parallelize(Array("1", "2", "3")).cache().collect()
          instance = sc.broadcast(dimMapTmp)
        }
      }
    }
    instance
  }
  
  def update(sc: SparkContext, blocking: Boolean = false): Unit = {
    if (instance != null)
      instance.unpersist(blocking)
    println("=====begin update=========")
    val dimMapTmp =  sc.parallelize(Array("3", "4", "5")).cache().collect()
    instance = sc.broadcast(dimMapTmp)
  }
}

用于定时执行的计数器

/**
  * Use this singleton to get or register an Accumulator.
  */
object UpdateTimeCount {
​
  @volatile private var instance: LongAccumulator = null
​
  def getInstance(sc: SparkContext): LongAccumulator = {
    if (instance == null) {
      synchronized {
        if (instance == null) {
          instance = sc.longAccumulator("UpdateTimeCount")
          //初始化时为当前时间
          instance.add( new Date().getTime)
        }
      }
    }
    instance
  }
}

业务类

object TimingUpdateDim {
​
  def main(args: Array[String]) {
​
    val conf = new SparkConf().setAppName("TimingUpdateDim").setMaster("local[2]")
    val ssc = new StreamingContext(conf, Seconds(5))
    ssc.sparkContext.setLogLevel("error")
    val lines = ssc.socketTextStream("192.168.222.129", 9999)
    // Split each line into words
    lines.foreachRDD(rdd=>{
      val dimTable = DimTable.getInstance(rdd.sparkContext)
      val currentAccumulatorInterval =  new Date().getTime - UpdateTimeCount.getInstance(rdd.sparkContext).value
      if(currentAccumulatorInterval>20000){
        DimTable.update(rdd.sparkContext)
        UpdateTimeCount.getInstance(rdd.sparkContext).add(currentAccumulatorInterval)
      }
      dimTable.value.map(print)
      println()
    })
    ssc.start()             // Start the computation
    ssc.awaitTermination()  // Wait for the computation to terminate
  }
}

测试效果

定时更新广播变量_第1张图片

 

总结

其实也可以判断时间,比如当天的9点到9点20来执行update操作,但是我考虑到生产的流处理批次是10s个批次,如果我将做update判断的时间设置得太短,有可能执行不到这个条件里面,如果设置得太长,driver端需要重复的cache,和unpersist。

问题

你可能感兴趣的:(定时更新广播变量)