我在akka-persistence上切齿,并找到了对象序列化的典型问题。我的对象(如下所示)具有基本类型和功能。我看过this,this和this,但没有人帮我制作以下序列化版本。
测试工具
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。
答案 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。