如何在Scala中序列化函数?

时间:2017-12-14 05:51:33

标签: java scala serialization akka-persistence

我在akka-persistence上切齿,并找到了对象序列化的典型问题。我的对象(如下所示)具有基本类型和功能。我看过thisthisthis,但没有人帮我制作以下序列化版本。

测试工具

object SerializationUtil {
  def write(obj: Any): String = {
    val temp = Files.createTempFile(null, null).toFile
    val out = new ObjectOutputStream(new FileOutputStream(temp))
    out.writeObject(obj)
    out.close()

    temp.deleteOnExit()
    temp.getAbsolutePath
  }

  def read[T](file: String) = {
    val in = new ObjectInputStream(new FileInputStream(new File(file)))
    val obj = in.readObject().asInstanceOf[T]
    in.close()
    obj
  }
}

统计

case class Stats(
                  app: String,
                  unit: ChronoUnit,
                  private var _startupDurations: List[Long]
                ) {
  def startupDurations = _startupDurations.sorted

  def startupDurations_=(durations: List[Long]) = _startupDurations = durations

  @transient lazy val summary: LongSummaryStatistics = {
    _startupDurations.asJava.stream()
      .collect(summarizingLong(identity[Long]))
  }
}

Stats序列化很好。

"SerializationUtil" should "(de)serialize Stats" in {
  val file = SerializationUtil.write(newStats())
  val state = SerializationUtil.read[Stats](file)

  verifyStats(state)
}

但这并不是:case class GetStatsForOneRequest(app: String, callback: Stats => Unit)

java.io.NotSerializableException: org.scalatest.Assertions$AssertionsHelper
    at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184)

也尝试过:

trait SerializableRunnable[T] extends scala.Serializable with ((T) => Unit)

将回调实现为SerializableRunnable的实例,但没有运气。

想法?

修改

也许我应该澄清在此问题中遇到的实际用例以提供更多上下文。该函数是来自Akka HTTP route的回调,如下所示:

path("stats") {
  logRequest("/stats") {
    completeWith(instanceOf[List[Stats]]) { callback =>
      requestHandler ! GetStatsRequest(callback)
    }
  }
}

handler actor会持续请求,直到获得响应。构建最终输出可能需要不止一个响应。

我做了一些挖掘,看起来回调实现是CallbackRunnable

1 个答案:

答案 0 :(得分:3)

也许您还没有完全理解链接的文章。函数序列化的问题是闭包中捕获的任何东西也必须是可序列化的。你需要的是Spores。一切都在那里解释,但这里有要点:

什么是封闭?

Scala中的Lambda函数可以引用外部作用域中的变量,而无需将它们明确地列为参数。执行此操作的函数称为闭包,它引用的外部变量是捕获。例如,在传递给foo下面的闭包中捕获map

val foo = 42
List(1,2,3).map(_ + foo)

为什么序列化会出现问题?

查看上面foo是原始值的示例,您不会认为这是一个问题。但是当有一个封闭的课时会发生什么?

class C {
  val myDBconn = ...
  val foo = 42
  List(1,2,3).map(_ + foo)
}

现在(对许多程序员来说意外),闭包捕获了非序列化封闭类的整个this,包括myDBconn,因为foo引用了getter方法{{1} }。

解决方案是什么?

解决方案是不在关闭中捕获this.foo。例如,为我们需要捕获的任何值创建本地this会使函数再次序列化:

val

当然,手动执行此操作非常繁琐,因此Spores