在Clojure中管理资源池的最佳方法

时间:2016-01-27 21:39:41

标签: clojure pool

我有一个使用Java库中可变资源的Web服务端点。该Web服务端点可以同时接收多个查询。 (端点使用Ring / Compojure实现)。创建这些资源成本很高,因此为每个Web服务调用重新创建它们实际上是低效的。

我想要做的是创建我在Web服务启动时填充的该资源的pool。然后,每次调用端点时,它都会从池中获取资源,将其用于处理,然后将其推回池中并等待下一次调用。

我想知道在Clojure中最好的方法是什么?是否有一个“池”Clojure库可以帮助我吗?

我天真地尝试使用原子中的向量来实现它,其中向量的每个项目都是该资源。然而,它很快就知道它不可能真的那样工作。

4 个答案:

答案 0 :(得分:3)

这是基于Timothy Pratley使用refs的想法:

(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

也许不是攻击这个的最好方法。但我喜欢它的简单性。

答案 1 :(得分:2)

要实施池,您需要解决2个问题:

  1. 并发。使用locking https://clojuredocs.org/clojure.core/locking或ref代替原子。请求可以是同时的,因此您需要注意两个消费者不可能获得相同的资源。

  2. 释放资源。考虑使用类似(with-open ...)的模式,即扩展到try-finally的宏,当你离开块范围时资源被释放回开放池。

  3. 您可能希望指定“错误”状态以及“可用”或“正在使用”,其中可能需要释放和重新创建资源。

答案 2 :(得分:2)

看看这个kul/pool。它使用Apache Commons Pool。我希望它有用。

答案 3 :(得分: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