2017-09-18 197 views
1

想象一下启动一个长时间运行的进程的请求,其输出是一大组记录。如何设计一个REST API来获取一个大的(临时的)数据流?

我们可以用一个POST请求启动该进程:

POST/API/V1 /长计算

输出包括编号记录的大序列,必须发送给客户。由于输出很大,服务器不存储所有内容,因此维护窗口大小上限的记录窗口。假设它可以存储多达1000条记录(并且只要这些记录可用,就会暂停计算)。当客户端获取记录时,服务器可能会随后删除这些记录,并继续生成更多记录(因为1000长度窗口中的更多插槽是免费的)。

比方说,我们取得与记录:?

GET/API/V1 /长计算ACK = 213

我们可以利用这意味着服务器将返回从开始记录索引214.当服务器接收到这个请求时,它可以假定(行为良好)客户端确认客户端收到213号的记录,所以它删除它们,然后将记录从214开始返回到任何当时可用。

下,如果客户端请求:

GET/API/V1 /长计算ACK = 214

服务器将删除记录214和返回记录从215

开始?

这似乎是一个合理的设计,直到发现GET请求需要安全且幂等(请参见HTTP RFC中的第9.1节)。

问题:

  1. 有没有更好的方式来设计这个API?
  2. 即使它看起来违反了标准,它仍然可以保持为GET吗?
  3. 难道是合理,使之POST请求如:

    POST/API/V1 /长计算/截断和取ACK = 213

+1

2616已过期。请改用RFC 723x。 –

+0

我与@Evert和@EricStein其他协议/样式可能更适合。尽管如此,HTTP为每个客户端返回相同输出的数据提供了一个简洁的流式解决方案 - [部分GET请求](https://tools.ietf.org/html/rfc7233)。虽然RFC或多或少地解释了字节范围,但像“Content-Range:”项目21-40/*“这样的自定义范围也是可能的,尽管您会错过确认。这必须以不同的方式完成。请注意,如果服务器能够为每个客户端计算相同的结果段,则服务器不需要存储整个结果 –

+0

@RomanVottner范围可以工作,但API仍然需要处理所请求范围尚未填充的情况,以及如何安全删除记录,以便创建更多记录。 –

回答

1

一个问题我总是觉得需要问的是,你确定REST是解决这个问题的正确方法吗?我是一个很大的粉丝和支持者REST,但是只尝试适用于适用的情况。

也就是说,我认为在使用资源后过期资源并不一定是错误的,但我认为重复使用同一个url的设计很糟糕。

相反,当我打电话的第一组结果(也许有):

GET /api/v1/long-computation 

我期望的资源给我的下一组结果的next链接。

虽然这个特殊的网址设计确实有点告诉我,整个系统在同一时间只有一个长计算。如果情况并非如此,我还希望在网页设计中有更多的独特性。

+0

感谢您的回复,我认为这对我来说是更实用的解决方案。将使用POST传递一个GET下一个记录+ DELETE较旧的记录类型的行动是设计此API的不好方法? – donatello

+0

@ donatello我认为这不是一个非常糟糕的方式来设计这个api,但我认为在他的观点你不能称它为REST了。我认为这是一个合理的设计,可能很多人会设计这个。从HTTP角度来看是正确的 – Evert

1

这里最好的解决方案是购买更大的硬盘。我假设你已经推倒了,这不是卡。

我认为你的操作是“不安全”,如RFC 7231所定义的,所以我建议不要使用GET。我还强烈建议您不要在没有客户端明确请求的情况下从服务器中删除记录。 REST建立的原则之一是网络不可靠。在您的设计下,如果出于任何原因而无法将答复交给客户,会发生什么情况?如果他们提出另一个请求,任何来自丢失响应的记录都将被销毁。

我要继续@ Evert的建议,你绝对必须保持这种设计,而是选择围绕可靠传递信息(例如消息队列)的技术。如果您要坚持使用REST,您需要允许客户告诉您何时可以安全删除记录。

例如,是否可以分页记录?您可以执行如下操作:

POST /long-running-operations?recordsPerPage=10 
202 Accepted 
Location: "/long-running-operations/12" 
{ 
    "status": "building next page", 
    "retry-after-seconds": 120 
} 

GET /long-running-operations/12 
200 OK 
{ 
    "status": "next page available", 
    "current-page": "/pages/123" 
} 
-- or -- 
GET /long-running-operations/12 
200 OK 
{ 
    "status": "building next page", 
    "retry-after-seconds": 120 
} 
-- or -- 
GET /long-running-operations/12 
200 OK 
{ 
    "status": "complete" 
} 

GET /pages/123 
{ 
    // a page of records 
} 

DELETE /pages/123 
// remove this page so new records can be made 

您需要在支持的记录数上标出页面大小。如果客户端请求小于此限制,则可以在处理第一页时处理更多记录。

这只是spitballing,但也许你可以从那里开始。没有质量承诺 - 这完全是我的头顶。这种方法有点琐碎,但是如果新页面还没有准备好,它可以避免返回404

+0

感谢您的回复 - 在我的问题结尾附近提到使用POST会导致严重的REST标准/ RFC违规? – donatello

+0

@ donatello关于RFC是很好的。如果请求经过但响应不成功,则问题仍然存在,数据“已完成”和“不可恢复”缺少重新启动整个长时间运行的过程。这是一个相当不利的用户体验。 –

相关问题