Spark自定义累加器的使用实例详解

 更新时间:2017年09月29日 11:32:21   作者:willian_zhang  
这篇文章主要介绍了Spark累加器的相关内容,首先介绍了累加器的简单使用,然后向大家分享了自定义累加器的实例代码,需要的朋友可以参考下。

累加器(accumulator)是Spark中提供的一种分布式的变量机制,其原理类似于mapreduce,即分布式的改变,然后聚合这些改变。累加器的一个常见用途是在调试时对作业执行过程中的事件进行计数。

累加器简单使用

Spark内置的提供了Long和Double类型的累加器。下面是一个简单的使用示例,在这个例子中我们在过滤掉RDD中奇数的同时进行计数,最后计算剩下整数的和。

val sparkConf = new SparkConf().setAppName("Test").setMaster("local[2]") 
val sc = new SparkContext(sparkConf) 
val accum = sc.longAccumulator("longAccum") //统计奇数的个数 
val sum = sc.parallelize(Array(1,2,3,4,5,6,7,8,9),2).filter(n=>{ 
 if(n%2!=0) accum.add(1L)  
 n%2==0 
}).reduce(_+_) 
println("sum: "+sum) 
println("accum: "+accum.value) 
sc.stop() 

结果为:

sum: 20
accum: 5

这是结果正常的情况,但是在使用累加器的过程中如果对于spark的执行过程理解的不够深入就会遇到两类典型的错误:少加(或者没加)、多加。

自定义累加器

自定义累加器类型的功能在1.X版本中就已经提供了,但是使用起来比较麻烦,在2.0版本后,累加器的易用性有了较大的改进,而且官方还提供了一个新的抽象类:AccumulatorV2来提供更加友好的自定义类型累加器的实现方式。官方同时给出了一个实现的示例:CollectionAccumulator类,这个类允许以集合的形式收集spark应用执行过程中的一些信息。例如,我们可以用这个类收集Spark处理数据时的一些细节,当然,由于累加器的值最终要汇聚到driver端,为了避免 driver端的outofmemory问题,需要对收集的信息的规模要加以控制,不宜过大。

继承AccumulatorV2类,并复写它的所有方法

package spark
import constant.Constant
import org.apache.spark.util.AccumulatorV2
import util.getFieldFromConcatString
import util.setFieldFromConcatString
open class SessionAccmulator : AccumulatorV2<String, String>() {
  private var result = Constant.SESSION_COUNT + "=0|"+
      Constant.TIME_PERIOD_1s_3s + "=0|"+
      Constant.TIME_PERIOD_4s_6s + "=0|"+
      Constant.TIME_PERIOD_7s_9s + "=0|"+
      Constant.TIME_PERIOD_10s_30s + "=0|"+
      Constant.TIME_PERIOD_30s_60s + "=0|"+
      Constant.TIME_PERIOD_1m_3m + "=0|"+
      Constant.TIME_PERIOD_3m_10m + "=0|"+
      Constant.TIME_PERIOD_10m_30m + "=0|"+
      Constant.TIME_PERIOD_30m + "=0|"+
      Constant.STEP_PERIOD_1_3 + "=0|"+
      Constant.STEP_PERIOD_4_6 + "=0|"+
      Constant.STEP_PERIOD_7_9 + "=0|"+
      Constant.STEP_PERIOD_10_30 + "=0|"+
      Constant.STEP_PERIOD_30_60 + "=0|"+
      Constant.STEP_PERIOD_60 + "=0"
  override fun value(): String {
    return this.result
  }
  /**
   * 合并数据
   */
  override fun merge(other: AccumulatorV2<String, String>?) {
    if (other == null) return else {
      if (other is SessionAccmulator) {
        var newResult = ""
        val resultArray = arrayOf(Constant.SESSION_COUNT,Constant.TIME_PERIOD_1s_3s, Constant.TIME_PERIOD_4s_6s, Constant.TIME_PERIOD_7s_9s,
            Constant.TIME_PERIOD_10s_30s, Constant.TIME_PERIOD_30s_60s, Constant.TIME_PERIOD_1m_3m,
            Constant.TIME_PERIOD_3m_10m, Constant.TIME_PERIOD_10m_30m, Constant.TIME_PERIOD_30m,
            Constant.STEP_PERIOD_1_3, Constant.STEP_PERIOD_4_6, Constant.STEP_PERIOD_7_9,
            Constant.STEP_PERIOD_10_30, Constant.STEP_PERIOD_30_60, Constant.STEP_PERIOD_60)
        resultArray.forEach {
          val oldValue = other.result.getFieldFromConcatString("|", it)
          if (oldValue.isNotEmpty()) {
            val newValue = oldValue.toInt() + 1
            //找到原因,一直在循环赋予值,debug30分钟 很烦
            if (newResult.isEmpty()){
              newResult = result.setFieldFromConcatString("|", it, newValue.toString())
            }
            //问题就在于这里,自定义没有写错,合并错了
            newResult = newResult.setFieldFromConcatString("|", it, newValue.toString())
          }
        }
        result = newResult
      }
    }
  }
  override fun copy(): AccumulatorV2<String, String> {
    val sessionAccmulator = SessionAccmulator()
    sessionAccmulator.result = this.result
    return sessionAccmulator
  }
  override fun add(p0: String?) {
    val v1 = this.result
    val v2 = p0
    if (v2.isNullOrEmpty()){
      return
    }else{
      var newResult = ""
      val oldValue = v1.getFieldFromConcatString("|", v2!!)
      if (oldValue.isNotEmpty()){
        val newValue = oldValue.toInt() + 1
        newResult = result.setFieldFromConcatString("|", v2, newValue.toString())
      }
      result = newResult
    }
  }
  override fun reset() {
    val newResult = Constant.SESSION_COUNT + "=0|"+
        Constant.TIME_PERIOD_1s_3s + "=0|"+
        Constant.TIME_PERIOD_4s_6s + "=0|"+
        Constant.TIME_PERIOD_7s_9s + "=0|"+
        Constant.TIME_PERIOD_10s_30s + "=0|"+
        Constant.TIME_PERIOD_30s_60s + "=0|"+
        Constant.TIME_PERIOD_1m_3m + "=0|"+
        Constant.TIME_PERIOD_3m_10m + "=0|"+
        Constant.TIME_PERIOD_10m_30m + "=0|"+
        Constant.TIME_PERIOD_30m + "=0|"+
        Constant.STEP_PERIOD_1_3 + "=0|"+
        Constant.STEP_PERIOD_4_6 + "=0|"+
        Constant.STEP_PERIOD_7_9 + "=0|"+
        Constant.STEP_PERIOD_10_30 + "=0|"+
        Constant.STEP_PERIOD_30_60 + "=0|"+
        Constant.STEP_PERIOD_60 + "=0"
    result = newResult
  }
  override fun isZero(): Boolean {
    val newResult = Constant.SESSION_COUNT + "=0|"+
        Constant.TIME_PERIOD_1s_3s + "=0|"+
        Constant.TIME_PERIOD_4s_6s + "=0|"+
        Constant.TIME_PERIOD_7s_9s + "=0|"+
        Constant.TIME_PERIOD_10s_30s + "=0|"+
        Constant.TIME_PERIOD_30s_60s + "=0|"+
        Constant.TIME_PERIOD_1m_3m + "=0|"+
        Constant.TIME_PERIOD_3m_10m + "=0|"+
        Constant.TIME_PERIOD_10m_30m + "=0|"+
        Constant.TIME_PERIOD_30m + "=0|"+
        Constant.STEP_PERIOD_1_3 + "=0|"+
        Constant.STEP_PERIOD_4_6 + "=0|"+
        Constant.STEP_PERIOD_7_9 + "=0|"+
        Constant.STEP_PERIOD_10_30 + "=0|"+
        Constant.STEP_PERIOD_30_60 + "=0|"+
        Constant.STEP_PERIOD_60 + "=0"
    return this.result == newResult
  }
}

方法介绍

value方法:获取累加器中的值

       merge方法:该方法特别重要,一定要写对,这个方法是各个task的累加器进行合并的方法(下面介绍执行流程中将要用到)

        iszero方法:判断是否为初始值

        reset方法:重置累加器中的值

        copy方法:拷贝累加器

spark中累加器的执行流程:

          首先有几个task,spark engine就调用copy方法拷贝几个累加器(不注册的),然后在各个task中进行累加(注意在此过程中,被最初注册的累加器的值是不变的),执行最后将调用merge方法和各个task的结果累计器进行合并(此时被注册的累加器是初始值)

总结

以上就是本文关于Spark自定义累加器的使用实例详解的全部内容,希望对大家有所帮助。有什么问题可以随时留言,小编会及时回复大家的。

相关文章

  • zerotier搭建免费moon服务器的部署流程

    zerotier搭建免费moon服务器的部署流程

    ZeroTier是一种基于P2P的虚拟组网工具,通过搭建Moon服务器‌可大幅提升跨运营商/跨国节点的连接质量,本文介绍了如何使用云服务部署ZeroTier的Moon服务器,并详细步骤包括登录服务器、安装ZeroTier、生成Moon配置文件、配置Moon服务器和重启服务,感兴趣的朋友一起看看吧
    2025-03-03
  • Cisco网络防火墙配置方法

    Cisco网络防火墙配置方法

    这篇文章主要介绍了Cisco网络防火墙配置方法,需要的朋友可以参考下
    2016-04-04
  • 忘记Grafana不要紧2种Grafana重置admin密码方法详细步骤

    忘记Grafana不要紧2种Grafana重置admin密码方法详细步骤

    这篇文章主要介绍了忘记Grafana不要紧2种Grafana重置admin密码方法详细步骤,需要的朋友可以参考下
    2022-04-04
  • pgpool-II搭建集群,实现高可用与读写分离

    pgpool-II搭建集群,实现高可用与读写分离

    pgpool-II是开源的PostgreSQL数据库连接池、负载均衡和高可用解决方案,支持多种工作模式,包括原始模式、内置复制模式和主/备模式,本文介绍pgpool-II的架构、进程、工作模式以及配置步骤,包括环境规划、系统准备、软件安装、数据库主节点配置、pgpool配置和集群启动
    2025-04-04
  • Centos中VNC远程桌面程序的安装与使用教程

    Centos中VNC远程桌面程序的安装与使用教程

    这篇文章主要介绍了Centos中VNC远程桌面程序的安装与使用的方法,较为详细的分析了CentOS的VNC远程桌面程序安装、配置、连接、启动等命令与相关操作技巧,需要的朋友可以参考下
    2016-07-07
  • rsync 常见错误与解决方法整理

    rsync 常见错误与解决方法整理

    由于我们经常使用rsync进行服务器文件的同步工作,但在配置过程中,会出现很多问题,下面的错误基本上都是通过客户端返回的错误进行分析
    2012-11-11
  • XAMPP下使用顶级域名绑定虚拟主机的配置方法和示例

    XAMPP下使用顶级域名绑定虚拟主机的配置方法和示例

    这篇文章主要介绍了XAMPP下使用顶级域名绑定虚拟主机的配置方法和示例,XAMPP是Windows下非常好用的一款集成开发环境,需要的朋友可以参考下
    2014-07-07
  • RSync实现文件备份同步详解

    RSync实现文件备份同步详解

    rsync,remote synchronize顾名思意就知道它是一款实现远程同步功能的软件,它在同步文件的同时,可以保持原来文件的权限、时间、软硬链接等附加信息
    2016-03-03
  • windows系统搭建zookeeper服务器的教程

    windows系统搭建zookeeper服务器的教程

    这篇文章主要介绍了windows系统搭建zookeeper服务器的教程,本文图文并茂给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下
    2019-10-10
  • curl.exe安装使用的最全参数详解以及常用命令汇总

    curl.exe安装使用的最全参数详解以及常用命令汇总

    Curl是一个功能强大的命令行工具,可以看做是命令行浏览器,用于与服务器进行数据交互,支持多种数据传输协议,如HTTP、HTTPS、FTP等,它支持文件的上传和下载,它是一款开源软件,在多个操作系统上均可运行,包括Windows、Linux、macOS等
    2024-04-04

最新评论