【问题标题】:Is an HTTP request 'atomic'HTTP 请求是“原子的”吗
【发布时间】:2017-03-02 06:01:38
【问题描述】:

我了解 HTTP 请求将产生带有代码和可选正文的响应。

如果我们将请求的发起者称为“客户端”,将请求的接收者称为“服务器”。

那么序列是

  1. 客户端发送请求
  2. 服务器收到请求
  3. 服务器发送响应
  4. 客户端接收响应

服务器是否有可能完成第 3 步但第 4 步没有发生(由于连接断开、应用程序错误等)。

换句话说:服务器是否有可能“相信”客户端应该收到响应,但由于某种原因客户端没有收到?

【问题讨论】:

  • 可能不是,因为 TCP
  • @Asu,实际上是的。因为 TCP。
  • 这取决于底层传输的可靠性,即使在支持 TCP 的端点之间,它在整个网络中也存在很大差异。您是否曾尝试访问一个繁忙的网站,但由于请求被丢弃而从未得到响应,但稍后再回来发现您的更改确实有效?
  • TCP 标准保证发送的数据包将被接收或发送者将收到错误,但并非所有实现都是平等的,它们也并非都完美地实现了标准。

标签: http


【解决方案1】:

网络本质上是不可靠的。只有对方确认了消息,你才能确定消息到达,但你永远不知道它没有。

更糟糕的是,对于 HTTP,请求的唯一确认是答案,并且没有对答案的确认。这意味着:

  • 客户端如果收到响应就知道服务器已经处理了请求。如果没有,它不知道请求是否已处理
  • 服务器永远不知道客户端是否得到了答案

TCP 堆栈通常会在关闭套接字时确认答案,但该信息不会传播到应用程序层并且在那里没有用处,因为堆栈可以确认收到,然后应用程序可能无论如何都不会处理该消息因为它崩溃(或电源故障或其他原因),并且从应用程序的角度来看,原因是在 TCP 堆栈中还是在它之上并不重要——无论是哪种方式消息都没有被处理。

处理这个问题的最简单方法是使用幂等操作。如果服务器再次收到相同的请求,它没有副作用并且响应是相同的。这样,如果客户端在等待响应时超时,只需再次发送请求,最终(除非连接被断开,再也无法修复)得到响应,请求就会完成。

如果一切都失败了,您需要记录执行的请求并消除服务器中的重复请求。因为没有网络协议可以为您做到这一点。它可以消除很多(就像 TCP 一样),但不是全部。

【讨论】:

  • 谢谢 Jan。我同意幂等可以解决这个问题。举一个简单的例子,如果请求是将服务器上的计数器增加 1,那么会有副作用。在这种情况下,您是否同意服务器上的重复数据删除是防止重复消息处理的唯一方法?
  • @MikeQ,是的,在这种情况下,只有在应用程序层的服务器上进行重复数据删除是唯一的解决方案。为此,您需要为请求提供适当派生的唯一 ID(uuid、客户端 ID + 来自客户端的唯一部分,或者通过单独的请求以未使用的 ID 无关紧要的方式获得的 ID)。
【解决方案2】:

在 HTTP RFC7230 6.6 Teardown 上有一个关于这一点的特定部分(加粗):

(...)

如果服务器立即关闭 TCP 连接,则 客户无法阅读最后一页的重大风险 HTTP 响应

(...)

为避免 TCP 重置问题,服务器通常会关闭连接 分阶段。首先,服务器通过仅关闭来执行半关闭 读/写连接的写端。然后服务器 继续从连接中读取,直到它收到一个 由客户端相应关闭,或直到服务器合理 确定它自己的 TCP 栈已经收到了客户端的 确认包含服务器最后一个数据包的 回复。最后,服务器完全关闭连接。

所以是的,这个响应发送步骤是一个相当复杂的东西。

例如查看此Apache 2.4 document 或复杂FIN_WAIT/FIN_WAIT2 pages for Apache 2.0 上的Lingering close 部分。

因此,一个好的 HTTP 服务器应该将套接字保持足够长的时间以合理地确定它在客户端是可以的。但是,如果您确实需要确认 Web 应用程序中的某些内容,则应使用回调(图像回调、ajax 回调)来断言响应已完全加载到客户端浏览器中(因此另一个 HTTP 请求)。这意味着它不是您所说的原子,或者至少不像您对关系数据库所期望的那样具有事务性。您需要添加来自客户端的另一个请求,也许您永远不会得到(因为服务器在收到确认之前已经崩溃)等等。

【讨论】:

    猜你喜欢
    • 2021-12-23
    • 2012-10-26
    • 1970-01-01
    • 2014-01-06
    • 1970-01-01
    • 2016-09-20
    • 2017-05-25
    • 2016-08-26
    • 2017-02-11
    相关资源
    最近更新 更多