Http-kit Clojure получает запрос, застрявший на нескольких асинхронных вызовах
Я создал небольшой пример, который выдвигает на первый план проблему:
(->> (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
библиотека журналов
2 ответа
Похоже, проблема вызвана тем, что пул потоков клиента http-kit не освобождает поток до тех пор, пока не будет завершена функция обратного вызова. И так в конечном итоге заканчивается поток.
Поэтому я начал думать, как сделать функцию обратного вызова быстрее, и придумал это решение:
Я создал асинхронную функцию-обертку для клиента 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 ")))))))
Моя текущая гипотеза состоит в том, что вы попадаете в тупик, исчерпав свой пул потоков:
- Вы создаете поток для каждого внешнего 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 запросов.
С уважением