保护非幂等 post 操作不被快速调用 Node.js?

Defending a non-idempotent post operation against being rapidly called in Node.js?

简短的问题:假设一个非幂等的 post 操作,您如何保护 node.js 中的 post 请求处理程序在响应之前被多次调用,因此导致数据损坏?

具体情况:我有一个匹配的 API,它需要大约 2-3 秒才能达到 return(因为必须 运行 通过大量用户群)。有许多操作,用户可以在同一秒内简单地两次调用它(这是一个错误,但不在我的控制之下,因此回答这部分并不构成对根本问题的回答)。在这些条件下,为用户选择了多个匹配项,这是不可取的。理想的结果是所有这些快速请求都具有相同的最终结果。

具体限制:

尝试使用 transaction。 如果您将所有 SQL 命令放入一个事务中,我认为这些请求将被分开。

您可以将所有请求推送到 queue。在这种情况下,您的所有回复都必须等待前面的回复完成。

另一种解决方案是使用 sequelize 事务,但这会导致数据库中 lock_wait_timeout 错误。

在我的 API 下,我对所有 REST 身份验证使用 express-jwt 和基于令牌的身份验证。我让这些令牌仅对一个请求有效。一旦使用令牌将被列入黑名单。

因此,即使您的客户发出多个请求,也只会接受第一个请求。其他人可以抛出错误 409 Conflict。一旦处理了第一个请求,新的 API 令牌将与响应一起发回,可能在 headers.

您的客户端必须不断更新每个响应的令牌。如果您在客户端使用 AngularJS 那使用 interceptors

非常容易

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

  • 为匹配项添加唯一键约束
  • 每个并行请求都尝试创建一个新的匹配项;由于约束
  • ,除了第一个以外的所有其他人都将无法这样做
  • 如果约束插入失败,应用程序只会提取数据库中已有的匹配项,将其添加到其余匹配项中,然后return将其

这使得无法通过快速调用 API 来垃圾邮件创建新的匹配项。但是,我对此并不满意,因为这种方法没有推广到例如:非确定性幂等操作(例如,如果匹配是随机生成的,随后的调用将 return 不同的匹配,因此是一个简单的约束检查是不够的)。

我所有的互联网点都不需要客户端票证管理(@Sushant),也不需要队列,并且可以处理不确定的幂等函数。

我提出了一个解决方案,其中服务器优雅地响应 same* 请求,因此不需要对客户端进行任何更改。

* 首先我们需要确定什么构成了被视为 "same" 的请求。当您计划这种优雅的同步响应时,您会在客户端请求中放置一个递增的计数器,这个计数器是将请求定义为 same 的唯一属性。但是由于您可能无法访问客户端并且没有这样的计数器,您可以将请求定义为相同,如果它们的 post-body + url 是相同的(并且可以抛出用户也需要相同)。

因此,对于每个用户,当请求到达您的服务器时,您会立即保存请求。一种简单的方法是散列 url + post-body,比如 SHA-256 并将其保存在这样的对象中: requests[user][hashOfRequest] = null 一旦您的服务器计算出来,null 将被替换为响应对象。现在您处理请求。

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

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

此模式也不再处理来自客户端的双重处理和连接断开(响应未到达客户端)。

当然,您应该在适合您的(客户端)场景的时间后清理响应哈希 table。可能在请求被放入哈希 table.

后 10 分钟