捍卫一个非幂等后期操作,以防止在Node.js中快速调用?

简单的问题:假设一个非幂等后期操作,你如何捍卫你的发布请求处理程序在多次调用node.js之前,他们可以响应,从而导致数据损坏?

具体案例:我有一个匹配的API,大约需要2-3秒才能返回(由于不得不通过一个大的用户库)。 有一些操作,用户可以简单地在同一秒内调用这个操作(这是一个错误,但不在我的控制之下,因此回答这个部分并不构成对根问题的回答)。 在这些条件下,为用户select了多个匹配,这是不可取的。 所有这些快速请求都会有相同的最终结果。

具体约束:

  • node.js / express / sequelize。

  • 如果我们添加一个队列,每个用户的请求将会在所有其他用户的请求之上,这可能在交通繁忙时产生严重的影响。

您可以将所有请求推送到队列中 。 在这种情况下,所有的答案都必须等待前面的答案才能完成。

另一种解决scheme是使用sequelize事务,但这会导致DB中的lock_wait_timeout错误。

我提出了一个解决scheme,服务器对same*请求进行优雅的响应,因此不需要对客户端进行更改。

*首先,我们需要确定什么构成被视为“相同”的请求。 当你计划这种优雅,同步,响应,你会把一个增加的计数器到客户请求,这个计数器是唯一的属性,定义一个请求same 。 但是由于您可能无法访问客户端,并且没有这样的计数器,所以如果他们的post url和url相同(并且可能导致用户需要相同),则可以将请求定义为相同。

因此,每个用户在到达服务器时立即保存请求。 一个简单的方法是散列url + post -body,比如SHA-256 ,并将其保存在如下对象中: requests[user][hashOfRequest] = null一旦服务器有了null它将被一个响应对象replace计算它。 现在处理请求。

如果客户端再次发送same*请求,您可以通过检查您的requests[user][hashOfRequest]轻松find。 如果服务器已经完成处理,它将包含一个你刚刚发送回客户端的响应对象。 如果它仍然是空的,你需要等待(第一个请求的)服务器的处理完成。 也许使用事件监听器或其他任务同步模式。

一旦服务器完成了第一个请求,它将生成response并将其保存在requests[user][hashOfRequest]=response并发出事件,以便潜在的等待客户端也能得到响应。

没有更多的双重处理和连接从客户端,其中响应没有达到他们,也是由这种模式处理。

当然,你应该在适合你的(客户)场景的时间之后清理响应哈希表。 也许10分钟后,请求被放入哈希表。

尝试使用交易 。 如果你把所有的SQL命令放到一个事务中,我认为这些请求是分开的。

在我的API下,我使用express-jwt和基于令牌的身份validation来进行所有REST身份validation。 我保持这些令牌只有一个请求有效。 一旦使用的令牌将被列入黑名单。

所以,即使您的客户发出多个请求,只有第一个将被接受。 其他人可以抛出错误409 Conflict 。 一旦处理了第一个请求,新的API令牌将随回应一起发回,可能在标题中。

您的客户必须不断更新每个响应中的令牌。 如果你在客户端使用AngularJS,使用拦截器很容易

我最终解决的解决scheme是@ Festo一般方法的变体,如下所示:

  • 为匹配添加唯一的键约束
  • 每个并行请求尝试创build新的匹配项; 由于约束,除了第一个之外,所有的都不会这样做
  • 如果约束插入失败,应用程序只是拉取已经在数据库中的匹配,将其添加到其余的匹配,并返回它

这使得垃圾邮件无法通过快速调用API来创build新的匹配。 然而,我并不满足于此,因为这种方法不能推广到例如非确定性幂等操作(例如,如果匹配是随机生成的,随后的调用将返回不同的匹配,因此简单的约束检查将是不充分的)。

我所有的互联网点的答案,不要求客户端票务pipe理(@Sushant),也不排队,并可以处理非确定性幂等函数。