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)))