RESTful监控REST资源变化的方式是什么?

43
如果有一个REST资源,我想监视其他客户端对其进行的更改或修改,最好(并且最符合REST原则)的做法是什么?
我考虑的一种方法是提供特定的资源来保持连接打开状态,而不是立即返回如果该资源不存在。例如,给定资源:
/game/17/playerToMove

对这个资源进行"GET"操作可能会告诉我现在轮到我的对手走了。如果不想反复轮询该资源以找出轮到我走的时机,我可以记下移动的编号(比如说5),并尝试检索下一步:

/game/17/move/5
在“正常”的REST模型中,似乎对于这个URL的GET请求会返回404(未找到)错误。然而,如果服务器保持连接开放直到我的对手下棋,即:
PUT /game/17/move/5

那么服务器可以返回我对手放入资源中的内容。这不仅能够为我提供所需的数据,而且还能作为一种通知方式,告诉我对手已经移动,而无需进行轮询。

这种方案是否符合RESTful原则?或者它违反了某些REST原则吗?


1
“Scrappy,有没有更好的方法来完成这个任务?” - Lightness Races in Orbit
你可以使用长轮询或将REST与WebSocket服务结合起来,向客户端发送事件。 - inf3rno
3个回答

30

你提出的解决方案听起来像是长轮询,这很可能会很有效。

你可以请求 /game/17/move/5,服务器将不发送任何数据,直到移动5完成。如果连接断开或超时,只需重新连接,直到获得有效响应。

这种方法的好处是非常快速的-一旦服务器有新数据,客户端就会立即收到。它也能够适应因连接断开而导致的问题,并且在客户端暂时断开连接后仍能正常工作(您可以在一个小时后请求/game/17/move/5并立即获取数据,然后进入move/6/等等)

长轮询的问题在于每个“轮询”都会占用一个服务器线程,这会迅速使Apache等服务器崩溃(因为它会用尽工作线程,无法接受其他请求)。您需要专门的Web服务器来处理长轮询请求。Python模块twisted(一种“事件驱动的网络引擎”)非常适合此项任务,但相对于常规轮询需要更多的工作。

关于Jetty/Tomcat的评论,我没有Java方面的经验,但它们似乎都使用类似于Apache的工作线程池系统,所以会出现相同的问题。我发现这篇文章似乎正好解决了Tomcat的这个问题。


我正在使用Jetty作为Java Servlet容器。对于“长轮询”,它似乎工作得很好。它是否有与Apache相同的问题(即,工作线程用尽)?那Tomcat呢? - Ross
为了避免线程占用,您可以使用asp.net异步HTTP处理程序。这将把线程还回给线程池。 - Steve Hibbert

4
如果您的目标客户是Web浏览器,我建议使用404错误代码,因为保持连接会主动阻止客户端对同一域的浏览器请求。客户端决定轮询的频率。
2021年编辑:上面的答案是在2009年给出的,仅供参考。
今天,我建议使用WebSocket接口进行推送通知。
或者,对于上述建议,我可能会建议在返回404之前在服务器上保持连接500-1000毫秒,并检查两次,以减少客户端创建多个连接的开销。

这有点滥用错误(你如何检测到实际的404,因为你请求的是/game/x14而不是/game/14)... 返回{'error':'没有新内容'}或其他类似的东西会更少出问题。 - dbr
3
不行,因为在完成第14步之前,该资源并不存在... 它是一个正确的404错误页面。 - Tracker1
如果预期使用情况是将来会出现,我建议响应503。根据规范,404不应自动重试。 - Mikko Rantalainen
@MikkoRantalainen 这不是503错误,"Service"是Web服务器,503通常是反向代理到其后面的资源已关闭或数据库服务器已关闭等情况的错误代码... 404是不存在的资源,这就是现在的情况。如果第14步从未发生,则它将永远不存在。404意味着该资源不存在,但并不意味着该资源永远不存在。 - Tracker1
我同意404看起来更好,因为它在逻辑上是“未找到”。然而,根据HTTP规范,404不应自动重试(4xx系列具有通用定义“客户端不应在没有修改的情况下重复请求。”)。当然,如果您有自定义服务和自定义客户端,可以随心所欲地弯曲规则。我想一个选项是响应404 + Expires: <suitable time in future>以表明资源现在不存在,但可能在过期时间后存在。我认为这个的确切解释还没有完全定义。 - Mikko Rantalainen
@MikkoRantalainen FYI:在规范方面,“SHOULD NOT”和“WILL NOT”并不相同。在上述情况下,这是经过设计的。 - Tracker1

2
我发现这篇文章提出了一种新的HTTP头部,名为"When-Modified-After",它基本上做的是相同的事情——服务器等待并保持连接打开,直到资源被修改。
我更喜欢基于版本的方法而不是基于时间戳的方法,因为它不太容易出现竞态条件,并且可以给你更多关于你正在检索的内容的信息。对于这种方法有什么想法吗?

1
版本控制方法将使用ETag HTTP标头。http://en.wikipedia.org/wiki/HTTP_ETag - Chase

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接