我有一个使用Java库中可变资源的Web服务端点。该Web服务端点可以同时接收多个查询。 (端点使用Ring / Compojure实现)。创建这些资源成本很高,因此为每个Web服务调用重新创建它们实际上是低效的。
我想要做的是创建我在Web服务启动时填充的该资源的pool
。然后,每次调用端点时,它都会从池中获取资源,将其用于处理,然后将其推回池中并等待下一次调用。
我想知道在Clojure中最好的方法是什么?是否有一个“池”Clojure库可以帮助我吗?
我天真地尝试使用原子中的向量来实现它,其中向量的每个项目都是该资源。然而,它很快就知道它不可能真的那样工作。
答案 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个问题:
并发。使用locking
https://clojuredocs.org/clojure.core/locking或ref代替原子。请求可以是同时的,因此您需要注意两个消费者不可能获得相同的资源。
释放资源。考虑使用类似(with-open ...)的模式,即扩展到try-finally的宏,当你离开块范围时资源被释放回开放池。
您可能希望指定“错误”状态以及“可用”或“正在使用”,其中可能需要释放和重新创建资源。
答案 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