如何防止Web服务API中的并发?

我们有三个Web服务( /a/b/c ),其中每个服务映射到一个单独的Java类( ClassAClassBClassC )中的方法( go() )。

只有一个服务应该同时运行(即: /b/a运行时无法运行)。 但是,由于这是一个REST API,因此无法阻止客户端请求同时运行的服务。

服务器上最好和最简单的方法是什么,以强制服务不同时运行?


更新 :这是一个内部应用程序,我们不会有大的负载,只有一个应用程序服务器。

更新 :这是一个主观问题,因为您可以对影响最终答案的一般应用程序设计提出不同的论点。 当我发现最有趣和最有帮助时,接受了翻译的回答。

假设只强制Web服务器只有一个监听线程提供请求是不行的……我想我只是使用一个静态锁( ReentrantLock可能为了清楚起见,尽管你可以在任何共享对象上实现同步):

 public class Global { public static final Lock webLock = new ReentrantLock(); } public class ClassA { public void go() { Global.webLock.lock() try { // do A stuff } finally { Global.webLock.unlock() } } } public class ClassB { public void go() { Global.webLock.lock() try { // do B stuff } finally { Global.webLock.unlock() } } } public class ClassC { public void go() { Global.webLock.lock() try { // do C stuff } finally { Global.webLock.unlock() } } } 

你的设计有缺陷。 服务应该是幂等的。 如果您所拥有的课程不支持,请重新设计,直到他们这样做。 听起来三种方法中的每一种都应该是服务的基础,而不是类。

首先,在不了解您的体系结构的情况下,如果必须对WebService层强制执行并发限制,则可能会遇到问题。 虽然您可以使用传统的锁等来串行化两个服务的请求,但是当您添加第二个Web层来扩展解决方案时会发生什么? 如果锁是Web层的本地锁,那么它们将是无用的。

我猜测可能有一层位于Web服务下面的某种层,你需要强制执行这些限制。 如果客户端B在客户端A发出冲突请求后进入,则后端应该在发现状态已更改时拒绝该请求,然后您应该将409返回给第二个客户端。 最终,竞争条件仍然存在,但您必须使用最低的公共层来保护您免受冲突的请求。

您可以使用某种信号量来保持对服务序列的访问。

为什么不使用超媒体来约束访问?

使用类似的东西,

 POST /A 

启动第一个过程。 完成后,结果应提供一个链接以启动第二个过程,

  Complete   

按照链接启动第二个过程,

 POST /B 

并重复部分C.

可以说,行为不当的客户端可以将链接缓存到步骤B,并尝试在将来的某个请求中重用它来规避序列。 但是,在执行步骤A时分配某种令牌并要求将令牌传递到步骤B和C以防止客户端手动构造URL并不困难。

进一步阅读你的评论,似乎你有一个情况,A可以在B之前或之后运行。在这种情况下,我建议创建一个资源D,代表整个过程集的状态(A,B和C) 。 当客户端检索D时,它会显示允许遵循的URI。 一旦客户端启动了A进程,则D资源应该在处理期间删除B链接。 当B在A之前启动时,应该发生相反的情况。

这种技术的另一个优点是很明显A或B是否已经运行了一天,因为状态可以在D中显示。一旦A和B运行,那么D可以包含C的链接。

超媒体不是一个100%万无一失的解决方案,因为你可能有两个客户端具有相同的D副本,并且两者都可能认为进程A尚未运行,并且两者都可能尝试同时运行它。 这可以通过在D上设置某种“上次修改”时间戳来解决,并且只要D的状态发生变化,您就可以更新该时间戳。 这可能允许后来的请求被拒绝。 基于您的场景的描述,似乎这更像是一个边缘情况,超媒体将捕获大多数并行运行进程的尝试。