Clojure 确保clj http'的正确方法;完成所有请求后,s的连接管理器将关闭

Clojure 确保clj http'的正确方法;完成所有请求后,s的连接管理器将关闭,clojure,core.async,clj-http,Clojure,Core.async,Clj Http,我有一个由cljhttp、core.async工具和atom组合而成的代码。它创建一些线程来获取和解析一组页面: (defn fetch-page ([url] (fetch-page url nil)) ([url conn-manager] (-> (http.client/get url {:connection-manager conn-manager}) :body hickory/parse hickory/as-hickory))) (def

我有一个由
cljhttp
core.async
工具和
atom
组合而成的代码。它创建一些线程来获取和解析一组页面:

(defn fetch-page
  ([url] (fetch-page url nil))
  ([url conn-manager]
    (-> (http.client/get url {:connection-manager conn-manager})
        :body hickory/parse hickory/as-hickory)))

(defn- create-worker
  [url-chan result conn-manager]
  (async/thread
    (loop [url (async/<!! url-chan)]
      (when url
        (swap! result assoc url (fetch-page url conn-manager))
        (recur (async/<!! url-chan))))))

(defn fetch-pages
  [urls]
  (let [url-chan (async/to-chan urls)
        pages (atom (reduce (fn [m u] (assoc m u nil)) {} urls))
        conn-manager (http.conn-mgr/make-reusable-conn-manager {})
        workers (mapv (fn [_] (create-worker url-chan pages conn-manager))
                      (range n-cpus))]
    ; wait for workers to finish and shut conn-manager down
    (dotimes [_ n-cpus] (async/alts!! workers))
    (http.conn-mgr/shutdown-manager conn-manager)

    (mapv #(get @pages %) urls)))
(defn获取页面
([url](获取页面url为零))
([url连接管理器]
(->(http.client/get url{:connectionmanager-conn-manager})
:body hickory/parse hickory/as hickory)))
(defn-创建工作进程)
[url chan结果控制经理]
(异步/线程)
(循环[url(异步/
我们的想法是使用多个线程来减少获取和解析页面的时间,但我不想让服务器过载,一次发送大量请求-这就是为什么使用连接管理器的原因。我不知道我的方法是否正确,建议是受欢迎的。目前的问题是,最后的请求失败是因为e连接管理器在终止之前关闭:
线程“async-thread-macro-15”java.lang.IllegalStateException中的异常:连接池关闭

主要问题:我如何在适当的时候关闭连接管理器(以及为什么我当前的代码无法做到这一点)?次要问题:我的方法正确吗?如果不正确,我该如何一次获取和解析多个页面,同时又不使服务器过载


谢谢!

问题是
async/alts!!
返回第一个结果(并且将继续这样做,因为
workers
从未更改)。我认为使用
async/merge
构建一个通道,然后重复读取它应该可以工作

(defn fetch-pages
  [urls]
  (let [url-chan (async/to-chan urls)
        pages (atom (reduce (fn [m u] (assoc m u nil)) {} urls))
        conn-manager (http.conn-mgr/make-reusable-conn-manager {})
        workers (mapv (fn [_] (create-worker url-chan pages conn-manager))
                      (range n-cpus))
        all-workers (async/merge workers)]
    ; wait for workers to finish and shut conn-manager down
    (dotimes [_ n-cpus] (async/<!! all-workers))
    (http.conn-mgr/shutdown-manager conn-manager)

    (mapv #(get @pages %) urls)))

我相信Alejandro关于错误原因的说法是正确的,这是合乎逻辑的,因为您的错误表明您已在所有请求完成之前关闭了连接管理器,因此在您关闭连接管理器时,很可能所有工作人员都未完成

我将提出的另一个解决方案源于这样一个事实,即在
create worker
线程中,您实际上没有做任何要求它成为通道的事情,而通道是由
async/thread
隐式创建的。因此,您可以用
future
替换它,如下所示:

(defn- create-worker
  [url-chan result conn-manager]
  (future
    (loop [url (a/<!! url-chan)]
      (when url
        (swap! result assoc url (fetch-page url conn-manager))
        (recur (a/<!! url-chan))))))
这就消除了大量的core.async干扰,而这些干扰本来就不是core.async问题。这当然取决于您保持收集数据的方法不变,即在atom上使用
swap!
来跟踪页面数据。如果要将
fetch page
的结果发送到返回通道,或者类似的东西,那么您希望保持当前的
线程方法

关于您对服务器过载的担忧,您尚未定义“过载”服务器的含义。这有两个方面:一个是请求速率(例如每秒请求数),另一个是并发请求的数量。您当前的应用程序有
n个
工作线程,这就是有效并发(以及连接管理器中的设置)。但这对解决每秒请求的速率没有任何作用


这比它看起来的要复杂得多,但你必须考虑每单位时间内所有线程所做的所有请求的总数,并且管理这不是一个答案。这里我建议你做一些关于节流和速率限制的研究,然后给出一个进展,然后再从那里开始。tions.

就是这样,谢谢!我不知道为什么,但我错误地认为alt!或alts!!能够“记住”以前选择的频道,但这毫无意义!
(defn- create-worker
  [url-chan result conn-manager]
  (future
    (loop [url (a/<!! url-chan)]
      (when url
        (swap! result assoc url (fetch-page url conn-manager))
        (recur (a/<!! url-chan))))))
(doseq [worker workers]
  @worker) ; alternatively, use deref to specify timeout