SparkCore中自定义累加器

累加器用来对信息进行聚合,通常在向 Spark 传递函数时,比如使用 map() 函数或者用 filter() 传条件时,可以使用驱动器程序中定义的变量,但是集群中运行的每个任务都会得到这些变量的一份新的副本,更新这些副本的值也不会影响驱动器中的对应变量。如果我们想实现所有分片处理时更新共享变量的功能,那么累加器可以实现我们想要的效果。

通过在驱动器中调用 SparkContext.accumulator(initialValue)方法,创建出存有初始值的累加器。返回值为 org.apache.spark.Accumulator[T] 对象,其中 T 是初始值 initialValue 的类型。Spark 闭包里的执行器代码可以使用累加器的 += 方法(在 Java 中是 add)增加累加器的值。 驱动器程序可以调用累加器的 value 属性(在 Java 中使用 value()或 setValue())来访问累加器的值。

注意:工作节点上的任务不能访问累加器的值。从这些任务的角度来看,累加器是一个只写变量。

自定义累加器类型的功能在 1.X 版本中就已经提供了,但是使用起来比较麻烦,在 2.0版本后,累加器的易用性有了较大的改进,而且官方还提供了一个新的抽象类:AccumulatorV2 来提供更加友好的自定义类型累加器的实现方式。实现自定义类型累加器需要继承 AccumulatorV2 并至少覆写下例中出现的方法。

package com.spark

import java.util.Set
import org.apache.log4j.{Level, Logger}
import java.util.Collections.unmodifiableSet
import org.apache.spark.util.AccumulatorV2
import org.apache.spark.{SparkConf, SparkContext}

object DefindAccumulator {
  def main(args: Array[String]): Unit = {
    Logger.getLogger("org").setLevel(Level.ERROR)
    // 创建spark配置信息
    val conf:SparkConf = new SparkConf().setAppName(this.getClass.getSimpleName).setMaster("local[*]")

    // 创建SparkContext
    val sc = new SparkContext(conf)

    // driver端的变量,声明一个累加器
    val accumulator = new LogAccumulator()
    // 自定义Accumulator,一定要注册,不然会抛出异常
    sc.register(accumulator, "logAccumulator")

    // 过滤掉带字母的
    val sum = sc.parallelize(Array("1", "2a", "3", "4b", "5", "6", "7cd", "8", "9"), 2)
    val res = sum.filter(line => {
      // 正则表达式,数字出现一次或多次
      val patttern = "^\\d+"
      // 匹配的全部是数字
      val flag = line.matches(patttern)
      if(!flag) {
        // 带字母的放到累加器中
        accumulator.add(line)
      } else {
        // 打印出纯数字的数据
        val result = line.toInt
        println("result:" + result)
      }
      flag
    })

    res.map(_.toInt).reduce(_+_)

    // 对累加器做遍历
    val array: Array[AnyRef] = accumulator.value.toArray
    for(i <- array) {
      print(i + ",")
    }

    sc.stop()
  }
}

/**
 * 第一个参数为输入类型,第二个参数为输出类型
 */
class LogAccumulator extends AccumulatorV2[String, Set[String]] {
  // 定义的集合,存放数据
  private val logArray: java.util.Set[String] = new java.util.HashSet[String]()

  /*
   * 告诉Spark程序,这个累加器对象是否是空的
   */
  override def isZero: Boolean = {
    logArray.isEmpty
  }

  /*
   * 提供给Spark框架一个拷贝的累加器
   */
  override def copy(): AccumulatorV2[String, Set[String]] = {
    val newAcc = new LogAccumulator()
    logArray.synchronized {
      newAcc.logArray.addAll(logArray)
    }
    newAcc
  }

  /*
   * 帮助Spark框架,清理累加器的内容
   */
  override def reset(): Unit = {
    logArray.clear()
  }

  /*
   * 外部传入要累加的内容,在这个方法中进行累加
   */
  override def add(v: String): Unit = {
    logArray.add(v)
  }

  /*
   * 累加器在进行累加的时候,可能每个分布式节点都有一个实例
   * 在最后Driver进行一次合并,把所有的实例的内容合并起来,会调用这个merge方法进行合并
   */
  override def merge(other: AccumulatorV2[String, Set[String]]): Unit = {
    other match {
      case o: LogAccumulator => logArray.addAll(o.value)
    }
  }

  /*
   * 提供给外部累加结果
   * 为什么一定要给不可变的?
   * 因为外部有可能再进行修改,如果是可变集合,其外部的修改会影响内部的值
   */
  override def value: Set[String] = {
    unmodifiableSet(logArray)
  }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值