Clojure http-kit 获取请求卡在多个异步调用上

Clojure http-kit get request stuck on multiple async calls

我创建了突出问题的小示例:

(->> (range 0 4)
     (mapv (fn [i]
             (http/get "http://http-kit.org/"
                       (fn [res]
                         (info "first callback")
                         (let [res2 @(http/get "http://http-kit.org/")]
                           (info "second callback ")))))))

它卡在打印 4s 第一次回调消息上。

如果我将范围更改为 0..3 它将起作用,同步版本也起作用。

更新:

(info) 是一个 taoensso.timbre 日志库

我目前的假设是您会因耗尽线程池而陷入死锁:

  1. 你为每个外层创建一个线程http/get
  2. 如果您创建的请求少于线程池中的可用线程,则至少有空间服务一个内部 http/get(这将需要一个新线程)
    1. 或者如果您的第一个请求在您耗尽线程池之前完成
  3. 一旦线程池中不再有线程,内部http/get就不能 得到服务
  4. 由于内层请求无法完成,外层永远卡死

您可以检查线程池的状态 http-kit 使用 peeking http/default-pool。在那里你可以看到类似的东西:

#object[java.util.concurrent.ThreadPoolExecutor 0x5a99e5c "java.util.concurrent.ThreadPoolExecutor@5a99e5c[Running, pool size = 8, active threads = 0, queued tasks = 0, completed tasks = 24]"]

当你没有陷入僵局的时候。或者

#object[java.util.concurrent.ThreadPoolExecutor 0x5a99e5c "java.util.concurrent.ThreadPoolExecutor@5a99e5c[Running, pool size = 8, active threads = 8, queued tasks = 8, completed tasks = 28]"]

当你这样做的时候。

我已经在我的机器上测试过了(显示 8 为 (.availableProcessors (Runtime/getRuntime))),我得到了上面的结果。当我 运行 超过 8 个请求时,我陷入了僵局。

此致

看起来问题是因为 http-kit 客户端线程池在回调函数完成之前不会释放线程。所以它以 运行 out of threads 结束。

所以我开始思考如何让回调函数更快,并想出了这个解决方案:

我已经为 http-kit 客户端创建了异步包装函数,它在回调中使用 clojure.core.async/chan 快速将结果放入通道,然后等待结果并执行重回调:

(defn async-http-get
  [url opts callback]
  (let [channel (chan)]
    (http/get url opts #(go (>! channel %)))
    (go (callback (<! channel)))
    nil))

所以现在使用 async-http-get 而不是 http/get 解决了我的问题。

(->> (range 0 4)
     (mapv (fn [i]
             (async-http-get "http://http-kit.org/"
                       (fn [res]
                         (info "first callback")
                         (let [res2 @(http/get "http://http-kit.org/")]
                           (info "second callback ")))))))