长期 RESTful 交互

Posted

技术标签:

【中文标题】长期 RESTful 交互【英文标题】:Long-lived RESTful interactions 【发布时间】:2010-09-12 07:40:14 【问题描述】:

目前我们的团队正在进行讨论,我会对其他观点感兴趣。假设我们有一个 RESTful Web 服务,其作用是通过应用各种分析算法和服务来注释文档。清晰的基本交互:我们有一个资源,即文档集合;客户端向集合发布一个新文档,获取新文档的 URI,然后可以获取 docURI 以获取文档,或获取 docURI/metadata 以查看一般元数据,docURI/ne 用于命名实体等. 问题是有些分析可能需要很长时间才能完成。假设客户端在分析完成之前获取元数据 URI,因为它希望能够在 UI 中显示部分或增量结果。以后重复 GET 可能会产生更多结果。

我们讨论过的解决方案包括:

保持 HTTP 连接打开 直到所有分析完成(其中 似乎不可扩展) 使用 content-lengthaccept-range 标题以获取增量内容(但 我们不知道提前多久 最终内容将是) 提供 每个资源的 Atom 提要 客户端订阅更新 事件而不是简单的 GETting 资源(似乎过度 如果有许多活动文档,则复杂且可能需要大量资源) 只有 GET 返回 当时可用的任何东西(但它仍然 留下客户的问题 知道我们什么时候最终完成)[编辑以删除对 cme​​ts 后幂等性的引用]

对于在 RESTful 架构中处理长期或异步交互的替代方法有何意见或建议?

伊恩

【问题讨论】:

确实有两种解决方案。简单的解决方案:使用GET,返回您当时拥有的任何元数据,如果仍在生成此数据,则将缓存生命周期标头设置为零。复杂的解决方案:使用 push 在生成元数据时将其发送给客户端...... imo 混乱,皮塔,不值得。使用GET 【参考方案1】:

您可能想查看Udi Dahan'snServiceBus。

【讨论】:

看起来很有趣。有人知道 Java 等价物吗?【参考方案2】:

一种可能适合也可能不适合您的情况的替代解决方案是添加一个名为“AnnotationRequests”的新端点。将文档(或指向它的链接)发布到 AnnotationRequests 端点,它应该返回一个位置(例如http://example.org/AnnotationRequest/2042),这将允许您的客户端轮询进程的状态。当该过程完成时,“AnnotationRequest”表示可以包含指向已完成文档的链接。

这样做的一个很好的副作用是您可以对 AnnotationRequests 执行 GET,以便查看当前正在处理的文档。由您决定要将 AnnotationRequests 保留多长时间。保留完整的历史记录可能很有价值,包括请求的时间、请求的对象和请求的时间,或者可以定期丢弃它们。

【讨论】:

【参考方案3】:

“让 GET 返回当时可用的任何东西”很有意义。除了,当他们进行投票时,你不想继续返回他们已经知道的东西。每次投票时答案都会变长。

您需要他们在 GET 请求中向您提供他们的“我目前所见”。这给了你幂等性。如果他们要求块 1,他们总是得到相同的答案。一旦他们看到了区块 1,他们就可以请求区块 2。

答案没有变大。更多作品可用。 “集合级” GET 提供响应的大小。对于每个可用的部分,您都有“详细级别”的 GET。

本质上,这是一种类似于 TCP/IP 确认的算法。当他们确认一块时,您发送下一块。如果有下一篇,则发送 200-nothing new 来报告。

“客户知道我们何时完成的问题”是无法估量的。他们不知道,你也无法预测需要多长时间。

您不希望他们“忙于等待”——轮询以查看您是否完成了——这对您的服务器来说是一个相当大的负载。如果他们不耐烦。您可以限制他们的请求。您可以向他们发送“x 秒后返回检查”,其中 x 会逐渐变大。

您甚至可以使用 Unix 风格的调度程序算法,当他们轮询时分数会下降,如果 X 秒不轮询,分数会上升。

另一种选择是某种队列,您可以将结果发回给他们。 为此,他们必须提供一个 URI,您可以通过 POST 告诉他们您已完成。

或者,他们将 Atom 用于轻量级轮询架构。虽然 Atom 看起来很复杂——它仍然涉及轮询——但你提供了一个最小的 Atom 答案(“尚未更改”)直到你完成,当你提供(“新结果”)时,他们可以做真正的 重量级搞定。这是全有或全无,而不是上面的增量响应技术。

您还可以将“集合级”GET 视为您在整个流程中的 Atom 状态。

【讨论】:

关于“你不想继续返回他们已经知道的东西”——HTTP 已经通过 304 响应处理了这个问题。关于“到目前为止我所看到的” - HTTP 已经通过 If-None-Match 处理了这个问题。你正在重新发明***。 不重新发明。这些是对结果进行编码的好方法。但是,您仍然必须实际计算结果,确定客户需要知道的内容,以便您可以在 200 或 304 之间做出决定。【参考方案4】:

为每个资源提供一个 Atom 提要,以便客户端订阅更新事件,而不是简单地获取资源(如果有许多活动文档,似乎过于复杂并且可能需要资源)

你考虑过SUP吗?

如果轮询是一种选择,为什么还要麻烦提要?为什么不让客户端轮询资源本身?

您能否通过包含完成分析的估计时间来减少不必要的轮询?

【讨论】:

嗨,吉姆,感谢您提供指向 SUP 的指针 - 我会做一些阅读。考虑提要而不是直接轮询的原因是,这将使客户更容易确定最近的更改并在 UI 中以不同的方式呈现这些更改。 显然您会比我更了解,但我不会想到识别更新的字段对客户来说会很困难。他们肯定可以将收到的数据与他们已有的数据进行比较吗? 是的,客户端可以自己进行比较(结果是RDF文档而不是字段,但该点仍然有效)。我还没有承诺任何一种方式,但只是想提一个提要可能具有优势的原因。临时混搭可能是另一种,虽然我还不相信!【参考方案5】:
只是忽略幂等性并让 GET 返回当时可用的任何内容(但仍然存在客户端知道我们何时最终完成的问题)。

随着时间的推移返回不同结果的 GET 真的意味着它不是幂等的吗? The spec 说:

方法还可以具有“幂等性”的属性,因为(除了错误或过期问题)N > 0 个相同请求的副作用与单个请求相同

也就是说,对 GET 的多次调用可能会返回不同的结果,只要调用本身没有副作用。

在这种情况下,也许您的 REST 方法可以使用条件 GET 和缓存机制来指示何时完成:

在分析过程中,GET docURI/metadata 响应可能具有: Expires 标头设置为未来几秒。 没有ETag 标头。 分析完成后,对该资源的响应具有: 没有Expires 标头。 ETag。使用ETag 的后续请求应返回304 Not Modified

注意,您可能需要考虑缓存中涉及的其他响应标头,而不仅仅是 Expires。

这种“感觉”就像一个 RESTful 设计 - 您可以想象一个 Web 浏览器在向该资源发出连续请求时正在做正确的事情。

【讨论】:

Matt(还有 Jim)- 是的,您对幂等性的看法是正确的,感谢您的澄清。我将编辑我的问题以消除潜在的混淆来源!【参考方案6】:

使用HTTP 202 Accepted。

另外,请查看RESTful Web Services - 这是我了解上述内容的地方。

【讨论】:

在同一个链接上,206 Partial Content 似乎适用。 @JeroenWyseur:不,“请求必须包含 Range 标头字段”。【参考方案7】:

我会通过以下方式实现它:

1) 客户端请求元数据 2) 服务器返回实际数据(如果它已经可用)或 NotReady 标记 3) 客户端询问服务器何时有数据可用(此步骤可与上一步合并) 4) 服务器返回时间间隔(可能有一些关于执行作业的总数等的启发式方法) 5) 客户端等待指定的时间并转到第 1 步

这样您可以尽快向客户提供数据。您可以通过调整第 4 步返回的延迟间隔来调整服务器负载)

【讨论】:

感谢您的回复。这将在客户端准备好后立即将所有数据提供给客户端,而我希望在数据可用时将数据增量呈现给 UI。 伊恩·狄金森,没必要。您可以返回部分数据,但将它们标记为不完整并提供轮询间隔。 @aku 另外,也许利用指数退避?

以上是关于长期 RESTful 交互的主要内容,如果未能解决你的问题,请参考以下文章

Restful接口对操作系统进行操作

RESTful

RESTful规范

RESTful 规范

黄聪:AngularJS中的$resource使用与Restful资源交互(转)

RESTful规范