如何在Scala中序列化函数? [英] How to serialize functions in Scala?

查看:1300
本文介绍了如何在Scala中序列化函数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我对akka持久性不屑一顾,并遇到了对象序列化的典型问题。我的对象(如下所示)具有基本的类型和功能。我阅读了,但没有一个帮助我使以下序列化。

I'm cutting my teeth on akka-persistence and came upon the quintessential problem of object serialization. My objects (shown below) have basic types, and functions. I read this, this and this, but none has helped me in making the following serializable.

测试实用程序

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 serializes just fine.

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

  verifyStats(state)
}

但这不是: case class Get StatsForOneRequest(app:String,callback:Stats =>单位)

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 的实例,但是没有运气。

implementing the callback as an instance of SerializableRunnable, but no luck.

想法?

编辑

也许我应该澄清这个问题的实际用例,以提供更多的上下文。该函数是来自Akka HTTP 路线,如下所示:

Perhaps I should clarify the actual use case that is running into this issue to provide more context. The function is a callback from Akka HTTP route like the following:

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

a href = https://github.com/asarkar/akka/blob/master/k8s-watcher/src/main/scala/org/abhijitsarkar/akka/k8s/watcher/web/RequestHandlerActor.txt rel = nofollow noreferrer>处理程序参与者会保留请求,直到获得响应为止。构造最终输出可能需要一个以上的响应。

The handler actor persists the request until it gets a response. It may take more than one response to construct the final output.

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

I did some digging and it appears that the callback implementation is a CallbackRunnable.

推荐答案

也许您不完全了解链接的文章。函数序列化的问题在于,闭包中捕获的所有内容也必须可序列化。您需要的是孢子。在那里解释了所有内容,但要点如下:

Maybe you didn't fully understand the linked articles. The problem with function serialization is that anything captured in the closure must also be serializable. What you need is Spores. Everything is explained there, but here is gist:

Scala中的Lambda函数可以引用外部作用域中的变量,而无需将其明确列出为参数。一个执行此功能的函数称为 closure ,它所引用的外部变量为 captured 。例如, foo 是在传递给以下 map 的闭包中捕获的:

Lambda functions in Scala can refer to variables in the outer scope without explicitly listing them as parameters. A function that does this is called a closure and the outer variables it refers to are captured. For example foo is captured in closure passed to map below:

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



为什么序列化有问题?



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

Why is it a problem for serialization?

Looking at the example above where foo is a primitive value, you woudn't think that's a problem. But what happens when there is an enclosing class?

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

现在(对于许多程序员而言这是意料之外的),该闭包捕获了不可序列化的封闭类的整个 this ,包括 myDBconn ,因为 foo 指的是getter方法 this.foo

Now (unexpectedly for many programmers) the closure captures the entire this of the non-serializable enclosing class, including myDBconn, because foo refers to the getter method this.foo.

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

The solution is to not capture this in the closure. For example creating a local val for any value we need to capture makes the function serializable again:

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

当然,手动执行此操作很麻烦,因此孢子

Of course, doing this manually is tedious, hence Spores.

这篇关于如何在Scala中序列化函数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆