Spark的对象不可序列化(object not serializable)的一个解决方案

2 篇文章 0 订阅

 最近在公司开发了一个新项目。这个项目要求人工去编写配置文件,然后根据配置文件配置的函数去处理数据。学到的隐式转换终于排上用场了。假设这些函数就是对比两个Set内容,然后得到这俩Set的相关得分。如下,然后分别实现这些函数即可。

 
  1. object ScoreFunction{

  2. implicit class FunctionGetterFaster(val functionConf: String) extends AnyVal {

  3. def getFunc: (Set[String], Set[String]) => Double = {

  4. functionConf match {

  5. case "numberCmp" => numberCmp

  6. case "dateCmp" => dateCmp

  7. case "setCmp" => setCmp

  8. case "sameChar" => sameChar

  9. case "editDistance" => editDistance

  10. case _ => (x: Set[String], y: Set[String]) => 0.0

  11. }

  12. }

  13. }

  14. }

注:implicit实现了一个隐式转换,将字符串转换为FunctionGetterFaster对象。extends AnyVal又是为了不发生实际的转换,这么折腾半天其实就是为了实现"dateCmp".getFunc。又不想花费构造对象的开销。

       但是经过思考,觉得这样做法总觉得不优雅。首先人工编写了配置文件了,已经指明了函数名,再匹配字符串,再获得函数,总觉得多了一步。并且最大的问题是如果我要拓展一个函数,总是要再在match中编写一个。自然而然就想到了反射机制:

 
  1. object ScoreFunction{

  2. import reflect.runtime.universe._

  3. implicit class FunctionReflector(val functionConf: String) extends AnyVal {

  4. def getFunc: (Set[String], Set[String]) => Double = {

  5. try{

  6. val mirror = runtimeMirror(getClass.getClassLoader)

  7. val instanceMirror = mirror.reflect(ScoreFunction)

  8. val methodSymbol = instanceMirror.symbol.toType.decl(TermName(functionConf)).asMethod

  9. val methodMirror = instanceMirror.reflectMethod(methodSymbol)

  10. def func(v1: Set[String], v2: Set[String]): Double = methodMirror(v1,v2).asInstanceOf[Double]

  11. func

  12. }catch {

  13. case _:Throwable => (x: Set[String], y: Set[String]) => 0.0

  14. }

  15. }

  16.  
  17. }

  18. }

       这里根据函数名取到并调用,返回结果是Any类型,所以要把它转成最后返回值格式。中间可能出现错误,找不到函数、找到的函数返回值类型不对,这里用了一个大大的try catch来解决。

       将对应的函数实现。并且编写了一个conf类里面存了一个map来存所有对应函数的引用地址。满心欢喜得意洋洋的运行了,结果傻眼了。Spark提示函数不可序列化:

object not serializable (class: scala.reflect.runtime.JavaMirrors$JavaMirror$JavaVanillaMethodMirror2, value: method mirror for def dateCmp...

       病急乱投医,尝试让ScoreFunction继承Serializable接口,还不行。尝试给这个隐式转换类、隐式转换类的方法、里面涉及的值添加@transient注解来不序列化它,还不行。将其恢复到第一个版本,不用反射就可以了。但是我怎么能满足于此呢,辛辛苦苦写的反射,就这么放弃么?最后尝试了半天,发现给反射的几个变量加上lazy居然就可以了!

 
  1. object ScoreFunction{

  2. import reflect.runtime.universe._

  3. implicit class FunctionReflector(val functionConf: String) extends AnyVal {

  4. def getFunc: (Set[String], Set[String]) => Double = {

  5. try{

  6. lazy val mirror = runtimeMirror(getClass.getClassLoader)

  7. lazy val instanceMirror = mirror.reflect(ScoreFunction)

  8. lazy val methodSymbol = instanceMirror.symbol.toType.decl(TermName(functionConf)).asMethod

  9. lazy val methodMirror = instanceMirror.reflectMethod(methodSymbol)

  10. def func(v1: Set[String], v2: Set[String]): Double = methodMirror(v1,v2).asInstanceOf[Double]

  11. func

  12. }catch {

  13. case _:Throwable => (x: Set[String], y: Set[String]) => 0.0

  14. }

  15. }

  16.  
  17. }

  18. }

       通过仔细推敲,之所以提示不可序列化是因为我的conf的函数配置分发到节点的时候需要进行序列化,而序列化的内容就是我最后这个func,这个func包含了一个MethodMirror,罪魁祸首就是它不可序列化。StackOverflow上面还有人提了相关问题。还有人说他提交给scala了一些代码希望它能可序列化(当然没有采用,所以我现在还不可序列化)。而加上了lazy,相当于不对这个值进行序列化了,而是把这个隐式转换对象。整个打包发送到worker上,然后用的时候才去加载,就不涉及对MethodMirror进行序列化了。理论上讲这种方式应该可以解决绝大多数的spark报的值、函数不可序列化问题。也许是自己孤陋寡闻,第一次发现lazy居然可以这样用,发出来分享,有大牛的话多多交流赐教。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值