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
日志库
我目前的假设是您会因耗尽线程池而陷入死锁:
- 你为每个外层创建一个线程http/get
- 如果您创建的请求少于线程池中的可用线程,则至少有空间服务一个内部
http/get
(这将需要一个新线程)
- 或者如果您的第一个请求在您耗尽线程池之前完成
- 一旦线程池中不再有线程,内部
http/get
就不能
得到服务
- 由于内层请求无法完成,外层永远卡死
您可以检查线程池的状态 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 ")))))))
我创建了突出问题的小示例:
(->> (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
日志库
我目前的假设是您会因耗尽线程池而陷入死锁:
- 你为每个外层创建一个线程http/get
- 如果您创建的请求少于线程池中的可用线程,则至少有空间服务一个内部
http/get
(这将需要一个新线程)- 或者如果您的第一个请求在您耗尽线程池之前完成
- 一旦线程池中不再有线程,内部
http/get
就不能 得到服务 - 由于内层请求无法完成,外层永远卡死
您可以检查线程池的状态 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 ")))))))