Clojure 为什么xml/会迅速地发出流,而直接写入流却不会
我正在尝试将文本以小块的形式写入流,当我将XML流编写器指向输出流(它立即开始发送数据)然后尝试写入一些文本,然后刷新它,直到流关闭为止,它不会发送任何内容时,这一工作正常Clojure 为什么xml/会迅速地发出流,而直接写入流却不会,clojure,Clojure,我正在尝试将文本以小块的形式写入流,当我将XML流编写器指向输出流(它立即开始发送数据)然后尝试写入一些文本,然后刷新它,直到流关闭为止,它不会发送任何内容时,这一工作正常 (defn data "Download a 5MB file and parse it" [] (-> "http://www.cs.washington.edu/research/xmldatasets/data/tpc-h/orders.xml" URL. .openStrea
(defn data
"Download a 5MB file and parse it"
[]
(-> "http://www.cs.washington.edu/research/xmldatasets/data/tpc-h/orders.xml"
URL.
.openStream
xml/parse))
(defn send-stuff [request]
(condp = (:uri request)
"/text" (response/response
(ring-io/piped-input-stream
#(let [w (io/make-writer % {:encoding "UTF-8"})]
(.write w "start\n")
(.flush w)
(Thread/sleep 1000)
(.write w "done\n")
(.flush w))))
"/xml" (response/response
(ring-io/piped-input-stream
#(->> (io/make-writer % {:encoding "UTF-8"})
(xml/emit (data))
.flush)))))
(comment
(def server (jetty/run-jetty #'send-stuff {:port 8888 :join? false}))
(.stop server))
用旋度测试,如下所示:
curl localhost:8888/text
静静地坐在那里整整一秒钟,然后回来
start
done
我希望看到“开始”,然后一秒钟后“完成”,而不是一秒钟的延迟,然后两者
和使用
curl localhost:8888/xml
立即开始流式传输刺眼的XML(很抱歉,个人偏见潜入其中;-)
--
编辑
我已经确认了jetty输出缓冲区的问题,因为如果我将该缓冲区设置得很小,它就会消失:
(def server (jetty/run-jetty #'send-stuff {:output-buffer-size 1 :port 8888 :join? false}))
当然,在许多情况下,将输出缓冲区设置为1是一个坏主意。您正在调用的
.flush
不是在用于HTTP响应的流上,而是在流上
当您查看时,您会注意到,它只通知所有等待从连接的PipedInputStream读取的线程,并不意味着刷新到底层HTTP响应流
行为上的差异是由响应数据大小引起的。如果将示例更改为使用小型XML数据,则行为将是相同的:
(defn data
[]
(-> "<?xml version=\"1.0\" encoding=\"UTF-8\"?><a>1</a>"
(.getBytes)
(ByteArrayInputStream.)
(xml/parse)))
(defn send-stuff [request]
(condp = (:uri request)
"/text" (response/response
(ring-io/piped-input-stream
#(let [w (io/make-writer % {:encoding "UTF-8"})]
(.write w "start\n")
(.flush w)
(Thread/sleep 1000)
(.write w "done\n")
(.flush w))))
"/xml" (response/response
(ring-io/piped-input-stream
#(let [w (io/make-writer % {:encoding "UTF-8"})]
(xml/emit (data) w)
(.flush w)
(Thread/sleep 1000)
(xml/emit (data) w)
(.flush w))))))
在它开始配管之前还是之后?我测试的目的是什么?嗯,关于如何进入环并刷新那里的输出缓冲区,有什么想法吗?我认为您可以使用自己的流机制(而不是管道输入/输出流)并通过扩展
ring.core.protocols/StreamableResponseBody
协议将其插入write body to stream
函数中的HTTP响应输出流。我最近提到过你有多棒吗?值得注意的是,刷新连接对的另一端(通过将其包装在另一个中间件中)也不能解决这个问题我假设这个例子对你有效,尽管我在返回相同的结果时遇到了一些问题,即使我删除了扩展协议块。
(ns so43769408
(:require [ring.adapter.jetty :as jetty]
[clojure.java.io :as io]
[ring.util.response :as response]
[ring.core.protocols :as protocols])
(:import (java.io OutputStream)
(java.util.concurrent LinkedBlockingQueue)))
(extend-protocol protocols/StreamableResponseBody
LinkedBlockingQueue
(write-body-to-stream [output-queue _ ^OutputStream output-stream]
(with-open [out (io/writer output-stream)]
(loop [chunk (.take output-queue)]
(when-not (= chunk ::EOF)
(.write out (str chunk))
(.flush out)
(recur (.take output-queue)))))))
(defn send-stuff [request]
(response/response
(let [output-queue (LinkedBlockingQueue.)]
(future
(.put output-queue "start\n")
(Thread/sleep 1000)
(.put output-queue "end\n")
(.put output-queue ::EOF))
output-queue)))
(comment
(def server (jetty/run-jetty #'send-stuff {:port 8888 :join? false}))
(.stop server))