SparkCore:序列化问题

第一种情况

假设我们数据需要与规则数据进行匹配,在Class中定义规则,此时不对它进行序列化。

Class Rules {
 val rulesMap = Map("hadoop" -> 1, "spark" -> 2)
val hostname = InetAddress.getLocalHost.getHostName
println(hostname + "@@@@@@@@@@@@@@@@")

}

如果我们在程序中使用map操作使规则和数据进行匹配。


    val r = lines.map(word => {
      val rules = new Rules
      //函数的执行是在Executor执行的(Task中执行的)
      val hostname = InetAddress.getLocalHost.getHostName
      val threadName = Thread.currentThread().getName
      //rules的实际是在Executor中使用的
      (hostname, threadName, rules.rulesMap.getOrElse(word, 0), rules.toString)
    })

结果:该函数在Executor的Task中执行的,每匹配一条数据进行map会创建一个rule对象,有多少条数据就会创建多少给rule实例对象,然后系统会不断对创建的对象进行GC,浪费大量资源。
(证明方法:我们把每个创建Rule实例的线程名字打印出来,发现每条数据线程不一样)

第二种情况

在上述情况基础上,我们在函数外面创建rule实例对象,然后在函数内部引用(闭包)。
假如rule类定义还如上所示,则会运行错误,抛出序列化错误。因为程序会在Driver端进行进行初始化,触发Action以后由后向前进行切分Stage,形成DAG,最后进行序列化通过Task传输至Executor中。所以此处如果Rule类没有继承序列化,在初始化过程中会在Driver端创建实例,无法发送至Executor中。
对Rules类进行序列化

Class Rules extends Serializable {
val rulesMap = Map("hadoop" -> 2.7, "spark" -> 2.2)
val hostname = InetAddress.getLocalHost.getHostName
println(hostname + "@@@@@@@@@@@@@@@@")
}

经过序列化后的Rules,我们在函数外创建的实例对象也会发送到Executor中

val rules = new Rule
.....
val r = lines.map(word => {
      //函数的执行是在Executor执行的(Task中执行的)
      val hostname = InetAddress.getLocalHost.getHostName
      val threadName = Thread.currentThread().getName
      //rules的实际是在Executor中使用的
      (hostname, threadName, rules.rulesMap.getOrElse(word, 0), rules.toString)
    })

最后观察结果可以发现,每个Task中都会产生一个Rules实例对象,所占用的线程互不相同。原因是,Task被发送到Executor中进行反序列化丢到线程池中执行。
在这里插入图片描述

第三种情况

Scala中class和Object的区别:class中所有成员变量和方法在被实例化之前是无法被使用的(包括main方法),而Object中所有变量和方法都是static,可以直接访问。特别注意:Object创建对象属于单例对象,一台机器一个进程中只能有一个Object实例对象。

在上述第二种情况基础上,我们想要每台机器中的Task都用同一份Rules,方法有两个:广播变量和调用Driver端定义的Object对象
但是广播变量的一个明显的缺点是:在Driver端定义好的规则在广播出去以后不能再进行修改,所以针对实时的规则方法,使用Object对象或者Redis。

函数内部调用Driver端中定义的Object:我们将规则的方法变量定义在Object中,Drvier端会在初始化过程中,在内存中创建Rules实例对象并随着Task创建发送到Exector中。同时在Exector中,只要有一个Task进行反序列化,就会在该机器的JVM进程中获得Rules这个单例对象,同一台机器上的Task用同一个rule对象。
证明方法:查看创建该对象的JVM进程,同一台机器上的进程号相同,不同机器且与Driver端都不同。

在这里插入图片描述

object Rules extends Serializable {
val rulesMap = Map("hadoop" -> 2.7, "spark" -> 2.2)
val hostname = InetAddress.getLocalHost.getHostName
println(hostname + "@@@@@@@@@@@@@@@@")
}
    //初始化object(在Driver端)
    var rules = Rules
    val r = lines.map(word => {
      //函数的执行是在Executor执行的(Task中执行的)
      val hostname = InetAddress.getLocalHost.getHostName
      val threadName = Thread.currentThread().getName
      //rules的实际是在Executor中使用的
      (hostname, threadName, rules.rulesMap.getOrElse(word, 0), rules.toString)
    })

第四种情况

上述第三种方法需要在Driver实例化规则,然后发送到各台机器上,需要网络传输。对这种方法再进行改善,只在Exector中初始化规则方法,且每台机器只有一个实例对象。
rule不在Driver端实例化,只在Exector中初始化rule实例对象。Objec中方法属性都是静态的,在Driver端定义了却没有使用,则不会类加载,所以不会在Driver端实例化,在Exector端进行任务执行时,调用该静态Object的方法属性时才会被加载,且同一台机器一个进程中只有一个实例对象,同时不需要网络传输实例对象


//第三种方式,希望Rules在EXecutor中被初始化(不走网络了,就不必实现序列化接口)
object Rules{
  val rulesMap = Map("hadoop" -> 2.7, "spark" -> 2.2)
  val hostname = InetAddress.getLocalHost.getHostName
  println(hostname + "@@@@@@@@@@@@@@@@!!!!")

}

  val r = lines.map(word => {
        //函数的执行是在Executor执行的(Task中执行的)
      val hostname = InetAddress.getLocalHost.getHostName
      val threadName = Thread.currentThread().getName
      //rules的实际是在Executor中使用的
      (hostname, threadName, Rules.rulesMap.getOrElse(word, 0), Rules.toString)
    })
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值