2016-01-27 3 views
7

У меня есть конечная точка веб-службы, которая использует изменяемый ресурс из библиотеки Java. Эта конечная точка веб-сервиса может одновременно получать несколько запросов. (конечная точка реализована с использованием Ring/Compojure). Создание этих ресурсов является дорогостоящим, поэтому повторное создание их для каждого вызова веб-службы действительно неэффективно.Лучший способ управления пулом ресурсов в Clojure

Что я хочу сделать, так это создать pool этого ресурса, который я заполняю при запуске веб-службы. Затем каждый раз, когда вызывается конечная точка, он берет ресурс из пула, использует его для его обработки и затем возвращает его в пул и ждет следующего вызова.

Мне интересно, что было бы лучшим способом сделать это в Clojure? Есть ли «пул» библиотека Clojure, которая могла бы помочь мне в этом?

Я наивно пытался реализовать это, используя вектор в атоме, где каждый элемент вектора является этим ресурсом. Однако он быстро понял, что это не может работать так.

+0

Если вы не против какой-то сверхмощный Java Interop, то [Apache Commons Pool] (https://commons.apache.org/proper/commons- pool /) библиотека всегда присутствует. – ez121sl

ответ

3

Это основано на идее Timothy Pratley «s использования рефов:

(def pool (ref ['a 'b 'c])) 

(defn take' [pool] 
    (dosync 
    (let [[h & t] @pool] 
     (ref-set pool (vec t)) 
     h))) 

(defn put [pool x] 
    (dosync 
    (alter pool conj x) 
    nil)) 

(take' pool) ;; => 'a 
(put pool 'a) ;; => nil 
(take' pool) ;; => 'a 
(take' pool) ;; => 'b 
(take' pool) ;; => 'c 

Может быть, не самый лучший способ борьбы с этим. Но мне нравится простота этого.

+0

Отлично, это именно то, что требовалось, спасибо! – Neoasimov

2

Для реализации пула Вам необходимо обратиться 2 проблемы:

  1. Параллелизм. Используйте lockinghttps://clojuredocs.org/clojure.core/locking или ref вместо атома. Запросы могут быть одновременными, поэтому вам нужно быть осторожными, чтобы два потребителя не могли получить тот же ресурс.

  2. Освобождение ресурсов. Подумайте об использовании шаблона типа (с открытием ...), то есть макроса, который расширяется до try-finally, где ресурс возвращается обратно в открытый пул, когда вы покидаете область блока.

Вы можете назначить статус «ошибки», а также «доступен» или «в работе», где ресурс может потребоваться быть освобожден и заново.

+0

Возможно ли использовать ref и не включать фактическую работу ресурса в транзакции? Потому что повторение этого было бы нежелательным. Пример был бы потрясающим. – muhuk

+0

В транзакции не может быть io (или побочных эффектов). Я не рекомендовал выполнять работу в транзакции, а скорее использовал ref или блокировку для обеспечения согласованности при доступе к списку ресурсов, их принятии и освобождении. Вы должны избегать блокировки чего-либо, пока ресурс используется, поскольку это может блокировать в течение длительного времени. Конечно, я могу добавить пример в ближайшее время. –

+0

О, я вижу, что вы уже сделали пример, круто :) –

2

Посмотрите на это kul/pool. Он использует Apache Commons Pool. Надеюсь, это полезно.

+0

Помните об этом, но я думаю, что предпочитаю идиоматический путь в clojure, спасибо! – Neoasimov

0

Вы также можете использовать атом:

(def pool (atom ['c 'b 'a])) 

(defn take' 
    [pool] 
    (loop [] 
    (let [p @pool] 
     (if (compare-and-set! pool p (pop p)) 
     (peek p) 
     (recur))))) 

(defn put 
    [pool x] 
    (swap! pool conj x) 
    nil) 

(take' pool) ;; => 'a 
(put pool 'a) ;; => nil 
(take' pool) ;; => 'a 
(take' pool) ;; => 'b 
(take' pool) ;; => 'c 
Смежные вопросы