REST服务和竞争条件

use*_*820 6 java rest race-condition

让我们想象一个问题:我有一个REST服务,它使用Java/MySQL/Spring和HTTP/JSON技术实现.REST服务的客户端是移动应用程序.所以有人可能会反编译代码并获得REST服务的API.(是的,代码是混淆等的,但无论如何).

问题:有一种POST方法可以向应用程序的其他用户汇款.我很担心,有人可以获得API,编写机器人并使此POST请求每秒500或5,000或甚至50,000次.结果,他可能会发送比实际更多的钱,因为如果同时处理1000个请求,那么对于所有1000个请求,余额检查可能会成功,但是一个帐户上的实际金额可能只够用于,假设,50个请求.

所以,基本上,它更像是具有多个线程的标准"竞争"条件.问题是,我有多台服务器,无论如何它们彼此无关.那么,300个请求可以来到服务器A,300个请求可以来到服务器B并且其他请求可以来到服务器C.

我最好的想法是使用"SELECT ... FOR UPDATE"之类的东西并在数据库级别上进行同步.但是,我想考虑另一种解决方案.

任何想法或建议?

med*_*088 2

您有几个选择:

  1. 依赖数据库的 ACID 实现(在您的例子中是 MySQL)。假设您使用的是 InnoDB 引擎,您需要选择正确的事务隔离级别(SET TRANSACTION 语法)并结合正确的锁定读取机制(SELECT ... FOR UPDATE 和 SELECT ... LOCK IN SHARE MODE 锁定读取)。您需要很好地理解这些概念才能做出正确的选择。即使没有锁定读取,简单地使用正确的隔离级别也可能已经防止竞争条件。缺点是您需要牺牲一致性来换取可扩展性,并将您的应用程序与 RDBMS 数据库绑定在一起,因此您将更难迁移到 NoSQL。

  2. 将后端分解为 Web 层和服务层(atk 在评论中建议的选项)。这将允许您独立扩展 Web 层实例,同时保留单个服务层实例。拥有单个服务层实例使得可以使用 Java 同步机制,例如synchronised块或ReadWriteLock. 尽管此解决方案可行,但我不会推荐它,因为它会降低服务层的可扩展性。

  3. 这是前一个选项的增强。您可以使用分布式锁管理器来代替内置的 java 同步机制。它将允许您独立扩展您的网络层和服务层。