Clojure/Jetty:强制URL一次只命中一次
我正在开发Clojure/Jetty web服务。我有一个特殊的url,我希望一次只为一个请求提供服务。如果url被请求,并且在返回之前,再次请求url,我想立即返回。因此,在我定义路线的more core.clj中,我有如下内容:Clojure/Jetty:强制URL一次只命中一次,clojure,locking,jetty,Clojure,Locking,Jetty,我正在开发Clojure/Jetty web服务。我有一个特殊的url,我希望一次只为一个请求提供服务。如果url被请求,并且在返回之前,再次请求url,我想立即返回。因此,在我定义路线的more core.clj中,我有如下内容: (def work-in-progress (ref false)) 然后过一会儿 (compojure.core/GET "/myapp/internal/do-work" [] (if @work-in-progress "Work
(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服务器上尝试过了,但是我似乎能够点击两次url,并且加倍了工作。在线程化web服务器环境中,在Clojure中实现这一点的好模式/方法是什么?想象一下问题中提出的解决方案
@work-in-progress
为false
,因此它进入do
表达式。但是,在它成功地将正在进行的工作
的值设置为真
之前@work-in-progress
为false,因此它进入do
表达式(执行工作)
。这不是我们想要的
要防止此问题,请在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"))
在这个场景中,您可能会发现另一个有用的抽象是atom和
实际上,这是锁的自然使用情形;特别是,一个 我在回答之前的一个问题时也出现了同样的模式;我将在这里重复相关的代码:
(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
,在这两种情况下都不阻塞。除了获得与锁/标志等效的功能外,还应考虑排队访问资源,队列可以让您观察资源争用以及其他优点。这真是太好了:我从未考虑过使用比较和设置
以事务方式获取有关原子的信息,同时将其更改为其他内容。您可能需要确保在(执行此工作)
引发异常的事件中释放“锁”,或者无论如何注意问题并采取措施。因此,我将用(尝试(完成工作)(最终(发布)))
或类似的方法(请参阅我的答案,以获得带有实际锁的完整代码片段)替换(完成(完成工作)(发布))
。非常感谢@MichałMarczyk。我已经确定了答案。
(import java.util.concurrent.locks.ReentrantLock)
(def lock (ReentrantLock.))
(defn start []
(if (.tryLock lock)
(try
(do-stuff)
(finally (.unlock lock)))
(do-other-stuff)))