Concurrency 在并发http kit/get实例中使用i/o回调的最简单方法
我正在启动数百个并发的Concurrency 在并发http kit/get实例中使用i/o回调的最简单方法,concurrency,clojure,core.async,http-kit,Concurrency,Clojure,Core.async,Http Kit,我正在启动数百个并发的httpkit.client/get请求,并提供了一个回调,用于将结果写入单个文件 处理线程安全性的好方法是什么?使用chan和来自core.asyc 这是我要考虑的代码: (defn launch-async [channel url]
httpkit.client/get
请求,并提供了一个回调,用于将结果写入单个文件
处理线程安全性的好方法是什么?使用chan
和来自core.asyc
这是我要考虑的代码:
(defn launch-async [channel url]
(http/get url {:timeout 5000
:user-agent "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:10.0) Gecko/20100101 Firefox/10.0"}
(fn [{:keys [status headers body error]}]
(if error
(put! channel (json/generate-string {:url url :headers headers :status status}))
(put! channel (json/generate-string body))))))
(defn process-async [channel func]
(when-let [response (<!! channel)]
(func response)))
(defn http-gets-async [func urls]
(let [channel (chan)]
(doall (map #(launch-async channel %) urls))
(process-async channel func)))
(defn启动异步[频道url]
(http/get url{:timeout 5000)
:用户代理“Mozilla/5.0(Macintosh;英特尔Mac OS X 10.12;rv:10.0)Gecko/20100101 Firefox/10.0”}
(fn[{:keys[status headers body error]}]
(如果有错误
(put!channel(json/generate字符串{:url:headers:status-status}))
(put!channel(json/generate string body(()()())))
(定义进程异步[通道函数]
(什么时候让我回答(
感谢您的见解。这很简单,我不会使用core.async。您可以使用一个atom来存储响应向量,然后让一个单独的线程读取atom的内容,直到看到所有响应。然后,在http工具包回调中,您可以将响应交换到汤姆直截了当地说
如果您确实想使用core.async,我建议使用一个缓冲通道来防止阻塞您的http kit线程池。这非常简单,我不会使用core.async。您可以使用存储响应向量的atom来实现这一点,然后让一个单独的线程读取atom的内容,直到看到所有响应为止。然后,在http工具包回调中,您可以将响应直接交换到atom中
如果您确实想使用core.async,我建议使用一个缓冲通道,以防止阻塞您的http工具包线程池。因为您在示例中已经使用了core.async,我想我应该指出一些问题以及如何解决这些问题。另一个答案提到使用更基本的方法,我完全同意更简单的ap但是,有了通道,你就可以使用一个简单的方法来消耗那些不涉及向量映射的数据,如果你有很多响应,它也会随着时间的推移而变大。考虑下面的问题以及我们如何修复它们:
(1) 如果url列表中的元素超过1024个,则当前版本将崩溃。有一个内部缓冲区用于异步的put和take(即,put!
和take!
不阻止,但始终立即返回),限制为1024。这是为了防止通道的无限异步使用。要亲自查看,请调用(http获取异步println(重复1025)http://blah-blah-asdf-fakedomain.com)
你想做的是,只有在有空间的时候才在频道上放置一些东西。这叫做背压。从优秀的wiki上获取一个页面,从http工具包回调中执行此操作的一个聪明方法是使用put!
callback选项启动下一个http get;这只会在put!
im时发生中间成功,因此您永远不会出现超出通道缓冲区的情况:
(defn launch-async
[channel [url & urls]]
(when url
(http/get url {:timeout 5000
:user-agent "Mozilla"}
(fn [{:keys [status headers body error]}]
(let [put-on-chan (if error
(json/generate-string {:url url :headers headers :status status})
(json/generate-string body))]
(put! channel put-on-chan (fn [_] (launch-async channel urls))))))))
(2) 接下来,您似乎只处理一个响应。相反,请使用go循环:
(defn process-async
[channel func]
(go-loop []
(when-let [response (<! channel)]
(func response)
(recur))))
现在,您可以在背压下处理无限多的URL。要测试这一点,请定义一个计数器,然后让您的处理功能增加此计数器以查看您的进度。使用易于打开的本地主机URL(不建议向google等发出成千上万的请求):
由于这都是异步的,您的函数将立即返回,您可以查看@响应
您可以做的另一件有趣的事情是,不必在process async
中运行处理函数,您可以选择将其作为通道本身的转换器应用
(defn process-async
[channel]
(go-loop []
(when-let [_ (<! channel)]
(recur))))
(defn http-gets-async
[func urls]
(let [channel (chan 10000 (map func))] ;; <-- transducer on channel
(launch-async channel urls)
(process-async channel)))
(defn进程异步
[频道]
(转到循环[]
(当)(
有很多方法可以做到这一点,包括构造它以使通道关闭(请注意,上面,它保持打开)。如果您愿意,您可以使用java.util.concurrent
原语来帮助您解决这方面的问题,而且它们非常容易使用。可能性非常大。因为您已经在示例中使用了core.async,我想我应该指出一些问题以及如何解决这些问题。另一个答案提到使用更基本的方法,即我完全同意一个更简单的方法是好的。但是,有了通道,你就有了一个简单的方法来消耗那些不涉及向量映射的数据,如果你有很多响应,它也会随着时间的推移而变大。考虑下面的问题以及我们如何修复它们:
(1) 如果url列表包含的元素超过1024个,则当前版本将崩溃。有一个内部缓冲区用于异步的put和take(即,put!
和take!
不阻塞,但始终立即返回),限制为1024。这是为了防止无限异步使用通道
(def responses (atom 0))
(http-gets-async (fn [_] (swap! responses inc))
(repeat 1000000 "http://localhost:8000"))
(defn process-async
[channel]
(go-loop []
(when-let [_ (<! channel)]
(recur))))
(defn http-gets-async
[func urls]
(let [channel (chan 10000 (map func))] ;; <-- transducer on channel
(launch-async channel urls)
(process-async channel)))