2024 Clojure状态调查!分享您的想法。

欢迎!请参阅关于页面以了解更多关于这个网站如何工作的信息。

0
Clojure
通过这次测试,我们可以看到,我们不能从协议中序列化方法(例如time-from-tweet),因为这会导致java.io.NotSerializableException: clojure.lang.MethodImplCache异常
 at java.io.ObjectOutputStream.writeObject0 (ObjectOutputStream.java:1183)
    java.io.ObjectOutputStream.defaultWriteFields (ObjectOutputStream.java:1547)
    java.io.ObjectOutputStream.writeSerialData (ObjectOutputStream.java:1508)
    java.io.ObjectOutputStream.writeOrdinaryObject (ObjectOutputStream.java:1431)
    java.io.ObjectOutputStream.writeObject0 (ObjectOutputStream.java:1177)
    java.io.ObjectOutputStream.writeObject (ObjectOutputStream.java:347)
    sparkling.protocol_test$serialize.invoke (protocol_test.clj:11)


这是实际的测试

(ns sparkling.protocol-test
  (:require [clojure.test :refer :all])
  (:import [java.io ObjectInputStream ByteArrayInputStream ObjectOutputStream ByteArrayOutputStream]))


(defn- serialize
  "将单个对象序列化,返回一个字节数组。"
  [v]
  (with-open [bout (ByteArrayOutputStream.)
                   oos (ObjectOutputStream. bout)]
    (.writeObject oos v)
    (.flush oos)
    (.toByteArray bout)))

(defn- deserialize
  "从给定的字节数组中反序列化和返回单个对象。"
  [bytes]
  (with-open [ois (-> bytes ByteArrayInputStream. ObjectInputStream.)]
    (.readObject ois)))


(defprotocol timestamped
  (time-from-tweet [item]))

(defrecord tweet [username tweet timestamp]
  timestamped
  (time-from-tweet [_]
    timestamp
    ))

(deftest sequable-serialization
  (testing "函数的序列化"
    (let [item identity]
      (is item (-> item serialize deserialize))))

  (testing "协议方法的序列化"
    (let [item time-from-tweet]
      (is item (-> item serialize deserialize)))))

6 个答案

0

评论来自:chrisbetz

顺便说一句:对于多方法也是这样,这里的异常是java.io.NotSerializableException: clojure.lang.MultiFn

0

评论来自:alexmiller

我认为我们不应该期望函数以这种方式是可序列化的。协议和多方法本质上都有基于扩展它们的实现运行时状态。这些函数序列化意味着什么?你会用在那个点上加载的任何实现来序列化它们吗?还是用没有实现?这两者对我来说都存在问题。常规函数是闭包,可以捕获它们环境的状态。我认为更好的答案是AOT,或者对于常规函数,类似于serializable-fn库的东西。

0

评论来自:chrisbetz

你好,

感谢您的评论。首先,有一些背景信息:我正在开发Sparkling,这是一个用于Apache Spark的Clojure API。它依赖AOT编译的函数来在集群中分发代码,因此是的,你不能简单地序列化任何函数,它需要AOT化。序列化为我们提供了对当前绑定等的支持,一切如期工作。所以,AFunction的可序列化是有原因的,其他的AFn/IFn实现也是如此,一切运行良好。

至于协议和多方法的状态 - 我认为它从概念上与函数的状态(哪个函数定义,可能绑定了多个var等)相同,以及绑定中给出的闭包。作为协议的用户,我没有理由相信协议中提供的方法与函数不同。事实上(ifn? protocol-method)也返回true。

serializable-fn,最初并不是为了经网络序列化而设计的,因此在与可序列化函数的绑定中的函数集合有关问题,以及在Spark环境中通过重复创建同一函数的类造成PermGen污染的问题。

我认为我现在还可以,因为我可以把协议方法包裹在函数中,但是我仍然相信,这是一个bug。

问候

Chris

0

评论来自:chrisbetz

实际上,这是造成问题的来自https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/AFunction.java的代码片段的链接。

`
public abstract class AFunction extends AFn implements IObj, Comparator, Fn, Serializable {

public volatile MethodImplCache __methodImplCache;
`

AFunction 可序列化,但 MethodImplCache 不可序列化。我不确定仅将其标记为 transient 是否足够,因为我没有检查初始化发生的位置。

0
by

评论来自:chrisbetz

我的邮件中的评论在 SMTP 的天涯海角丢失了:有一个简单的解决方案。将协议方法包裹在一个函数中,这将让您达到目的,但会使得您的代码变得更加糟糕 ;)

0
by
参考:https://clojure.atlassian.net/browse/CLJ-1701 (由 alex+import 提出)
...