【问题标题】:Prevent REST clients of simultaneous executing same methods with same arguments防止 REST 客户端同时执行具有相同参数的相同方法
【发布时间】:2017-08-28 02:06:08
【问题描述】:

考虑Spring MVC java web-application,它提供了一些REST API

假设它有很多方法,其中之一是DELETE /api/foo/{id},它显然会从数据库中删除foo 给定id 的实体。

问题是由于数据库中的大数据,这个操作不是立即的,所以如果客户端尝试同时对同一个实体执行多次删除操作,比如说

DELETE /api/foo/123 x N 次(当然是客户端软件错误),

它会在数据库中引起一些令人不快的副作用(你知道,如果你尝试在多个事务中删除相同的实体,那通常不是很好)。

我的问题是:Spring MVC 中防止此类情况的最佳做法是什么?

我当然可以在每个这样的更新方法 (PUT/DELETE) 中引入 Foo id 上的同步。我需要为所有实体和所有PUT/DELETE API 方法执行此操作,但我真的不想这样做。我想它应该是一些优雅而漂亮的解决方案,如何在拦截器/servlet级别执行这种类型的同步,即不在控制器级别的服务上。

我还可以创建特定的拦截器并在那里等待重复的请求(具有相同 URL 和参数的请求)。但同样,这听起来不是一个优雅的解决方案(直到我确保无法以某种更美观的方式在 Spring MVC 中进行配置)。

【问题讨论】:

  • 原则上,事务机制就是为此目的而设计的。您还有哪些进一步要求将事务回滚视为数据库中令人不快的副作用
  • 是的,问题是不同的数据库对此类事件有不同的行为。就我而言,由于复杂的复制结构,我遇到了某种阻塞。我通常不想从数据库中捕获和接收任何类型的此类异常,我想阻止它们的出现。
  • 我是对还是错。我不知道据我所知我们可以做一件事,只是检查数据库中的数据是否可用,或者不像 getFooById(id);如果你得到的数据比你可以删除相同的数据,否则你可以抛出数据不存在的异常

标签: java spring rest api spring-mvc


【解决方案1】:

这是一个并发问题,应通过使用适当的事务和锁定级别来处理。不幸的是,这里没有适合所有情况的单一大小,根据您的实际需求,您可能必须实现乐观或悲观锁定,以及可能的事务级别之一(从根本没有事务到可序列化事务)。

一般来说,在网络级别处理此类问题是一个坏主意,因为您最终会遇到这样的问题,例如根据请求要做什么想要删除其他人同时显示的一些数据?在 SpringMVC 中,常见的方式是在服务层使用事务方法。另外,您应该在持久层中声明一个乐观或悲观锁定系统。

乐观层通常会提供更高的吞吐量,但代价是某些事务以异常结束。在这种情况下,当前的最佳做法是向用户报告问题,要求他/她再次发送他/她的请求。

【讨论】:

  • 这种方法无法解决我目前的麻烦。重负载数据库中相同原始的多个 DELETE 请求会导致某种阻塞,因此所有这些多个请求都失败并出现 504 超时错误。当然,所有删除方法本身都是在事务级别执行的。但它不包括 DB 本身行为不当的情况。
  • 504 删除请求超时错误通常是无法获得(悲观)锁的症状。但这是优化数据库访问的困难部分,它在很大程度上取决于数据库本身和主要用例。很抱歉,但恐怕我不能说更多。也许您可以尝试用乐观锁替换悲观锁,以将 504 超时错误与诸如 System is busy and could not complete your request, please try again.. 之类的消息交换
  • 那不合适。正如我在问题中所写,直接的方法是在特定实体的 ID 上引入同步。这将简单地防止数据库被锁定,因此超时异常不会有任何麻烦 - 顺序执行的 DELETE 方法将很快返回 OK,因为在我的 API 中删除不存在的实体是合法操作。我的问题是:是否有可能使用 Spring MVC 框架的现有功能在更高级别上执行相同的技巧。
  • @Andremoniy:除了 Spring 级别的事务之外,我不知道任何事情。而在 Web 层面,没有什么比拦截器更好的了……
猜你喜欢
  • 2016-06-16
  • 2013-10-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多