Clojure / Jetty:强制URL只能一次命中一次

时间:2013-06-05 19:07:11

标签: clojure locking jetty

我正在开发Clojure / Jetty网络服务。我有一个特殊的网址,我希望一次只能为一个请求提供服务。如果请求了url,并且在它返回之前,再次请求url,我想立即返回。所以在更多core.clj中,我定义了我的路由,我有类似的东西:

(def work-in-progress (ref false)) 

然后某个时候

(compojure.core/GET "/myapp/internal/do-work" []
    (if @work-in-progress
        "Work in Progress please try again later"
        (do
            (dosync
                (ref-set work-in-progress true))
            (do-the-work)
            (dosync
                (ref-set rebuild-in-progress false))
            "Job completed Successfully")))

我在本地Jetty服务器上尝试了这个,但我似乎能够两次点击网址并将工作翻倍。在线程Web服务器环境中,在Clojure中实现这一点的好方法/方法是什么?

3 个答案:

答案 0 :(得分:4)

想象一下问题中提出的解决方案的后续race condition

  1. 线程A开始执行处理程序的主体。 @work-in-progressfalse,因此输入do表达式。但是,在设法将work-in-progress的值设置为true ...
  2. 之前
  3. 线程B开始执行处理程序的主体。 @work-in-progress为false,因此输入do表达式。
  4. 现在两个线程同时执行(do-the-work)。那不是我们想要的。

    要防止此问题,请在dosync事务中检查并设置ref的值。

    (compojure.core/GET "/myapp/internal/do-work" []
      (if (dosync
            (when-not @work-in-progress
              (ref-set work-in-progress true)))
        (try
          (do-the-work)
          "Job completed Successfully"
          (finally
            (dosync
              (ref-set work-in-progress false))))
        "Work in Progress please try again later"))
    

    在这种情况下,您可能会发现另一个有用的抽象是原子和compare-and-set!

    (def work-in-progress (atom false))
    
    (compojure.core/GET "/myapp/internal/do-work" []
      (if (compare-and-set! work-in-progress false true)
        (try
          (do-the-work)
          "Job completed Successfully"
          (finally
            (reset! work-in-progress false)))
        "Work in Progress please try again later"))
    

答案 1 :(得分:2)

实际上这是锁的自然用例;特别是java.util.concurrent.locks.ReentrantLock

在我对早期SO问题的回答中出现了同样的模式Canonical Way to Ensure Only One Instance of a Service Is Running / Starting / Stopping in Clojure?;我将在这里重复相关的代码:

(import java.util.concurrent.locks.ReentrantLock)

(def lock (ReentrantLock.))

(defn start []
  (if (.tryLock lock)
    (try
      (do-stuff)
      (finally (.unlock lock)))
    (do-other-stuff)))

tryLock方法尝试获取锁定,如果成功则返回true,否则返回false,在任何一种情况下都不会阻塞。

答案 2 :(得分:1)

考虑排队对资源的访问 - 除了获得与锁/标志相同的功能之外,队列还可以让您观察资源争用,以及其他优点。