Spark 闭包与序列化(json4s.jackson)

Spark的官方文档再三强调那些将要作用到RDD上的操作,不管它们是一个函数还是一段代码片段,它们都是“闭包”,Spark会把这个闭包分发到各个worker节点上去执行,这里涉及到了一个容易被忽视的问题:闭包的“序列化”。

显然,闭包是有状态的,这主要是指它牵涉到的那些自由变量以及自由变量依赖到的其他变量,所以,在将一个简单的函数或者一段代码片段(就是闭包)传递给类似RDD.map这样的操作前,Spark需要检索闭包内所有的涉及到的变量(包括传递依赖的变量),正确地把这些变量序列化之后才能传递到worker节点并反序列化去执行

问题

Exception in thread "main" org.apache.spark.SparkException: Job aborted due to stage failure: Task not serializable: java.io.NotSerializableException: org.json4s.DefaultFormats$
Serialization stack:
	- object not serializable (class: org.json4s.DefaultFormats$, value: org.json4s.DefaultFormats$@5897047d)
	- field (class: com.demo.cn.mlib.SparkJSON$$anonfun$1, name: formats$1, type: class org.json4s.DefaultFormats$)
	- object (class com.demo.cn.mlib.SparkJSON$$anonfun$1, <function1>)
	- field (class: org.apache.spark.sql.execution.MapPartitionsExec, name: func, type: interface scala.Function1)
	- object (class org.apache.spark.sql.execution.MapPartitionsExec, MapPartitions <function1>, obj#19: scala.Tuple2
+- DeserializeToObject createexternalrow(content#8.toString, StructField(content,StringType,true)), obj#18: org.apache.spark.sql.Row
   +- LocalTableScan [content#8]

其中extract方法为

def extract[A](implicit formats : org.json4s.Formats, mf : scala.reflect.Manifest[A]) : A = { /* compiled code */ }

问题的症结就在于:闭包没有办法序列化。在这个例子里,闭包的范围是:函数parser以及它所依赖的一个隐式参数: formats , 而问题就出在这个隐式参数上, 它的类型是DefaultFormats,这个类没有提供序列化和反序列自身的说明,所以Spark无法序列化formats,进而无法将task推送到远端执行。

解决方法

实际上我们根本不需要序列化formats, 对我们来说,它是无状态的。所以,我们只需要把它声明为一个全局静态的变量就可以绕过序列化。所以改动的方法就是简单地把implicit val formats = DefaultFormats的声明从方法内部迁移到App Object的字段位置上即可。

最终代码:

package com.demo.cn.mlib

import com.alibaba.fastjson.JSONArray
import org.apache.spark.sql.SparkSession
import com.alibaba.fastjson.JSON

import scala.collection.mutable.ArrayBuffer
import org.json4s._
import org.json4s.jackson.JsonMethods._

object SparkJSON {

  implicit val formats = DefaultFormats

  def main(args: Array[String]): Unit = {

    val spark=SparkSession.builder()
      .appName("PiPlineLR")
      .master("local[*]")
      .config("spark.serializer","org.apache.spark.serializer.KryoSerializer")
      .getOrCreate()

    import spark.implicits._
    val training = spark.createDataFrame(
      Seq(
        ("1001","{\"name\":\"Tom\", \"age\":25}","12"),
        ("1002","{\"name\":\"Jack\", \"age\":20}","19"),
        ("1003","{\"name\":\"Andy\", \"age\":34}","14")

      )).toDF("id","content","age")

    val tmpDF=training.selectExpr("content").mapPartitions(x=>{
      x.map(x=>{
       val json= x.getAs[String]("content")
        val mess=parse(json).extract[info]
        (mess.name,mess.age)
      })
    }).toDF("name","age")

//    tmpDF.repartition(1).write.parquet("/")

    tmpDF.show()
    spark.stop()

  }

  case class info(name:String,age:Int)

}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值