【问题标题】:Clojure http-kit get request stuck on multiple async callsClojure http-kit 获取请求卡在多个异步调用上
【发布时间】:2017-07-28 16:24:37
【问题描述】:

我创建了一个突出问题的小例子:

(->> (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 日志库

【问题讨论】:

  • 我在运行时没有发现任何问题。你得到一个包含 4 个 derefables 的向量,打印了 4 个“第一个回调”,并打印了 4 个“第二个回调”。作为健全性检查,请记住 (range 0 4) 是 4 个元素,而不是 5 个。
  • @Josh 你可以尝试将范围更改为 10 个元素吗?
  • @Josh 我认为它可能因不同机器而异,取决于 CPU 内核。对我来说,我刚刚重新启动了我的 mac,并得到了 4 个 derefables 的响应,打印了 4 个“第一个回调”然后卡住了。

标签: clojure http-kit


【解决方案1】:

我目前的假设是,您会通过耗尽线程池进入死锁:

  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 个请求时,我陷入了僵局。

问候

【讨论】:

  • 是的,我也认为这是一个问题,你能重现这个问题吗?
  • 是的,在我的 (.availableProcessors (Runtime/getRuntime)) 为 8 的框中,如果我运行超过 8 个请求,它们就会卡住。
【解决方案2】:

看起来问题是因为 http-kit 客户端线程池在回调函数完成之前不会释放线程。所以它最终会用完线程。

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

我已经为 http-kit 客户端 get 创建了异步包装函数,它在回调中使用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 ")))))))

【讨论】:

    猜你喜欢
    • 2018-04-10
    • 1970-01-01
    • 2023-03-06
    • 2011-01-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多