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 ")))))))

введите описание изображения здесь

Моя текущая гипотеза состоит в том, что вы попадаете в тупик, исчерпав свой пул потоков:

  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 запросов.

С уважением

Другие вопросы по тегам