【问题标题】:Getting the same instance of RandomAccessFile in clojure在 clojure 中获取相同的 RandomAccessFile 实例
【发布时间】:2015-05-06 07:20:59
【问题描述】:

这段代码在服务器上运行,它检测文件的更改并将其发送到客户端。这是第一次工作,之后文件长度没有得到更新,即使我更改了文件并保存了它。我想clojure 的不变性是这里的原因。我怎样才能做到这一点?

 (def clients (atom {}))
    (def rfiles (atom {}))
    (def file-pointers (atom {}))

(defn get-rfile [filename]
  (let [rdr ((keyword filename) @rfiles)]
    (if rdr
      rdr
      (let [rfile (RandomAccessFile. filename "rw")]
        (swap! rfiles assoc (keyword filename) rfile)
        rfile))))

(defn send-changes [changes]
  (go (while true
        (let [[op filename] (<! changes)
              rfile (get-rfile filename)
              ignore (println (.. rfile getChannel size))
              prev ((keyword filename) @file-pointers)
              start (if prev prev 0)
              end (.length rfile) // file length is not getting updated even if I changed the file externally
              array (byte-array (- end start))]
          (do
            (println (str "str" start " end" end))
            (.seek rfile start)
            (.readFully rfile array)
            (swap! file-pointers assoc (keyword filename) end)
            (doseq [client @clients]
              (send! (key client) (json/write-str
                                    {:changes  (apply str (map char array))
                                     :fileName filename}))
              false))))))

【问题讨论】:

    标签: clojure functional-programming immutability randomaccessfile clojure-java-interop


    【解决方案1】:

    这里没有不变性。在rfiles 原子中,您存储 可变的标准Java 对象。

    只有当数据追加到文件末尾并且大小总是增加时,此代码才能正常工作。

    如果文件中有更新/添加(长度为+N)而不是末尾,指针 startend 不会指向修改后的数据,但只到最后 N 个字符,您将向客户端发送虚拟内容。

    如果有删除或任何减少长度的更改,

    array (byte-array (- end start))
    

    会抛出一个你看不到的NegativeArraySizeException(被go 集团吃掉了?)。您可以添加一些 (try (...) catch (...)) 或测试 (- end start) 始终为正或为空,以管理它并执行适当的行为:重置指针?,...

    您确定您扫描的更改文件仅通过附加数据进行更改吗?如果没有,您需要通过相应地重置或更新指针来处理这种情况。

    我希望它会有所帮助。


    编辑测试环境。

    我定义了以下内容。您提供的代码没有变化。

    ;; define the changes channel
    (def notif-chan (chan))
    
    ;; define some clients
    (def clients (atom {:foo "foo" :bar "bar"}))
    
    ;; helper function to post a notif of change in the channel
    (defn notify-a-change [op filename]
      (go (>! notif-chan [op filename])))
    
    ;; mock of the send! function used in send-changes
    (defn send! [client message]
      (println client message))
    
    ;; main loop
    (defn -main [& args]
      (send-changes notif-chan))
    

    在一个 repl 中,我跑了:

    repl> (-main)
    

    在 shell 中(我也用编辑器测试过):

    sh> echo 'hello there' >> ./foo.txt
    

    在repl中:

    repl> (notify-a-change "x" "./foo.txt")
    str0 end12
    :bar {"changes":"hello there\n","fileName":".\/foo.txt"}
    :foo {"changes":"hello there\n","fileName":".\/foo.txt"}
    
    repl> (notify-a-change "x" "./foo.txt")
    str12 end12
    :bar {"changes":"","fileName":".\/foo.txt"}
    :foo {"changes":"","fileName":".\/foo.txt"}
    

    在外壳中:

    sh> echo 'bye bye' >> ./foo.txt
    

    在一个副本中:

    repl> (notify-a-change "x" "./foo.txt")
    str12 end20
    :bar {"changes":"bye bye\n","fileName":".\/foo.txt"}
    :foo {"changes":"bye bye\n","fileName":".\/foo.txt"}
    

    【讨论】:

    • 感谢您的回复。我确定我在文件末尾附加了数据。如果我每次都创建一个新的 RandomAccessFile 而不是缓存文件对象,则此代码会将附加的更改发送到客户端。 (println (str "str" start " end" end)) 告诉我,一旦创建了文件,它的大小就保持不变。所以我想可能是 get-rfile 总是给一个新的副本。
    • 我在本地测试了代码,当我将一些数据附加到文件中时(例如echo "hello" &gt;&gt; ./foo.txt 或使用编辑器),然后我将更改发布到changes 频道,更改为检测并记录正确的信息。当我重复这个过程时,它工作正常。我不认为问题来自get-rfilefile-pointers 如果没有其他进程在处理数据文件...
    • 这很有趣。我为此使用 TextEdit。我会尝试使用不同的编辑器,看看是否有什么不同。 (我认为不会)。
    • 在不同进程中更新文件时如何将更改发布到频道?你能分享你的代码吗?
    • 这一点我从 repl 手动执行:1.更新文件,2.发布更改。我不知道你是如何在你的系统中做到这一点的,因为代码不包含这部分。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-05-26
    • 2012-09-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多