2016-01-27 15 views
7

Ho un endpoint di un servizio Web che utilizza una risorsa mutabile da una libreria Java. L'endpoint del servizio Web può ricevere più query contemporaneamente. (l'endpoint è implementato usando Ring/Compojure). La creazione di queste risorse è costosa, quindi la loro creazione per ogni chiamata al servizio Web è davvero inefficiente.Il modo migliore per gestire un pool di risorse in Clojure

Quello che voglio fare è creare uno pool di quella risorsa che popolino all'avvio del servizio web. Quindi, ogni volta che viene chiamato l'endpoint, prende una risorsa dal pool, la utilizza per l'elaborazione, quindi la reinserisce nel pool e attende che si verifichi la prossima chiamata.

Mi chiedo quale sarebbe il modo migliore per farlo in Clojure? Esiste una libreria "pool" di Clojure che potrebbe aiutarmi?

Ho cercato ingenuamente di implementarlo utilizzando un vettore in un atomo in cui ogni elemento del vettore è quella risorsa. Tuttavia, ha rapidamente imparato che non potrebbe funzionare in questo modo.

+0

Se non ti dispiace un po 'pesanti Java interoperabilità, il [Apache Commons Pool] (https://commons.apache.org/proper/commons- pool /) la biblioteca è sempre lì. – ez121sl

risposta

3

questo si basa su un'idea Timothy Pratley s' di utilizzare arbitri:

(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 

Forse non è il modo migliore per attaccare questo. Ma mi piace la semplicità.

+0

Ottimo, questo è esattamente ciò che è stato richiesto, grazie! – Neoasimov

2

per implementare una piscina è necessario affrontare le preoccupazioni 2:

  1. concorrenza. Utilizzare lockinghttps://clojuredocs.org/clojure.core/locking o un riferimento invece di un atomo. Le richieste possono essere simultanee, quindi è necessario fare attenzione che sia impossibile per due consumatori ricevere la stessa risorsa.

  2. Liberare risorse. Prendi in considerazione l'uso di un modello come (con-open ...), cioè una macro che si espande in un try-finally in cui la risorsa viene rilasciata nuovamente al pool aperto quando lasci l'ambito del blocco.

Si potrebbe voler indicare uno stato di 'errore' così come 'disponibile' o 'in uso', in cui la risorsa può avere bisogno di essere rilasciato e ricreati.

+0

E 'possibile utilizzare un ref e non includere il lavoro effettivo della risorsa all'interno della transazione, però? Perché ritentare su quello sarebbe indesiderabile. Un esempio sarebbe fantastico. – muhuk

+0

Non possono esserci effetti io (o effetti collaterali) all'interno di una transazione. Non stavo raccomandando di fare il lavoro nella transazione, ma piuttosto usando il ref o il locking per forzare la coerenza quando accedevo alla lista delle risorse, prendendole e rilasciandole. Dovresti evitare di bloccare qualsiasi cosa mentre la risorsa è in uso in quanto può potenzialmente bloccarsi per un lungo periodo. Certo, posso aggiungere un esempio al più presto. –

+0

Oh, ti vedo già fatto un esempio, figo :) –

2

Dai un'occhiata a questo kul/pool. Utilizza Apache Commons Pool. Spero sia utile

+0

Lo terrò a mente, ma penso che preferisco il modo idiomatico in clojure, grazie! – Neoasimov

0

È inoltre possibile utilizzare un atomo:

(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 
Problemi correlati