TL;DR
- REST 和 HTTP 都适合批量操作。
- 通常,缓存是 REST 约束之一,不是可选的而是强制的,它会阻止某种形式的批处理。
- 不将要批量更新或删除的数据公开为自己的资源,而是将其公开为单个资源中的数据元素(如 HTML 页面中的数据表),这可能是有益的。这里更新或删除全部或部分条目应该是直接的。
- 如果系统总体上是写入密集型的,那么最好考虑其他解决方案,例如将数据库直接暴露给这些客户端,以节省进一步的间接性和复杂性。
- 利用缓存可以避免服务器上的大量工作负载,甚至可以节省不必要的连接
首先,REST 和 HTTP 都适合批量操作。正如吉姆·韦伯指出的。这就是 HTTP 所做的事情,也是它所擅长的。然而,我们得出的任何业务规则都只是文档管理的副作用,我们必须提出解决方案,将这种文档管理副作用转化为有用的东西。
由于 REST 只是可浏览 Web 中使用的概念的概括,因此适用于 Web 开发的相同概念也以某种形式适用于 REST 开发也就不足为奇了。因此,诸如应该如何在 REST 中完成某些操作的问题通常会围绕回答如何在 Web 上完成某些操作来解决。
如前所述,HTTP 在批处理操作方面并不理想。当然,一个 GET 请求可能会检索多个结果,但实际上您会获得一个包含指向更多资源的链接的响应。根据《资源的创造》HTTP规范 https://www.rfc-editor.org/rfc/rfc7231#section-4.3.3,用a表示Location
指向新创建的资源的标头。POST
被定义为一种通用方法,允许根据服务器特定的语义执行任务。所以你基本上可以用它来一次创建多个资源。然而,HTTP 规范显然缺乏对指示同时创建多个资源的支持Location
header 对于每个响应只能出现一次,并且在其中只能定义一个 URI。那么服务器如何向服务器指示多个资源的创建呢?
HTTP 不适用于批处理的进一步迹象是 URI 必须引用单个资源。尽管 URI 不可能同时指向多个资源,但该资源可能会随着时间而改变。 URI 本身或多或少被缓存用作密钥,缓存存储该 URI 的可缓存响应表示形式。由于 URI 可能仅引用一个资源,因此缓存也仅存储该 URI 的一个资源的表示形式。如果对 URI 执行不安全的操作,缓存将使存储的 URI 表示无效。如果出现DELETE
操作,本质上是不安全的,URI 的表示DELETE
执行后将被删除。如果您现在“重定向”DELETE
一次删除多个后备资源的操作,缓存应该如何注意这一点?它仅对调用的 URI 进行操作。因此,即使您一次性删除多个资源DELETE
缓存可能仍然为客户端提供过时的信息,因为它根本没有注意到删除,并且它的新鲜度值仍然表明足够新鲜的状态。除非您默认禁用缓存,这在某种程度上违反了其中之一REST 的限制 https://restfulapi.net/rest-architectural-constraints/#cacheable,或者将表示被认为足够新鲜的时间段减少到非常低的值,客户可能会得到过时的信息。当然,您可以对每个 URI 执行不安全的操作,然后“清除”缓存,尽管在这种情况下您可以调用DELETE
对您想要批量删除自身的每个资源进行操作。
不过,如果您要删除的一批数据不是通过其自己的资源显式捕获而是作为单个资源的数据捕获,那么它会变得更容易一些。想象一下网页上的数据表,其中有某些表单元素,例如您可以单击复选框将条目标记为删除候选者,然后在调用提交按钮后将相应的选定元素发送到执行操作的服务器删除这些项目。这里仅更新一个资源的状态,因此一个简单的POST
, PUT
甚至PATCH
可以对该资源 URI 执行操作。这也很适合前面概述的缓存,因为只需更改一个资源,通过在该 URI 上使用不安全的操作将自动导致给定 URI 的任何存储表示形式失效。
然而,上述使用表单元素来标记某些要删除的元素取决于所发布的媒体类型。对于 HTML 来说,它的表格部分 https://www.w3.org/TR/html52/sec-forms.html指定可用的组件及其。可供性是指你可以并且应该对某些对象做什么的知识。 IE。按钮或链接可能需要被按下,文本字段可能需要数字或字母数字输入,其进一步可能受到长度限制等等。其他媒体类型,例如哈尔形式 https://rwcbook.github.io/hal-forms/, halform http://halform.herokuapp.com/ or ion https://ionspec.org/#forms,尝试为基于 JSON 的表示法提供表单表示和组件,但是,对此类媒体类型的支持仍然相当有限。
由于您关心的问题之一是与服务的客户端连接数量,我假设您有一个写入密集型场景,因为在读取密集型情况下,缓存可能会带走服务器的很大一部分负载。 IE。 BBC 曾报道称,他们只需为最近请求的资源引入一分钟的缓存间隔即可大幅减少服务器上的负载。这主要影响了他们的起始页和链接文章,因为人们点击最新新闻的频率高于旧新闻。如前所述,每分钟收到数千甚至数十万个请求时,他们可以显着减少实际到达服务器的请求数量,从而减轻服务器上的巨大负载。
然而,写入密集型用例无法像读取密集型用例那样充分利用缓存,因为缓存经常会失效,并且实际请求会转发到服务器进行处理。如果 API 或多或少用于执行 CRUD 操作(正如现实中许多“REST”API 所做的那样),那么将数据库直接暴露给客户端是否更好是值得怀疑的。几乎所有现代数据库供应商都提供了复杂的用户权限管理选项,并允许创建可以向某些用户公开的视图。在这种情况下,其之上的“REST API”基本上只是增加了进一步的间接性和复杂性。通过直接公开数据库,执行批量更新或删除根本不应该成为问题,因为通过相应的查询语言对此类操作的支持应该已经构建到数据库层中。
关于客户端创建的连接数量: HTTP 从 1.0 开始允许通过以下方式重用连接:Connection: keep-alive https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Connection标头指令。在 HTTP/1.1 中,如果没有通过相应的显式请求关闭,则默认使用持久连接。Connection: close
标头指令。 HTTP/2 引入了全双工连接,允许多个通道,因此请求可以同时重用相同的连接。这或多或少是对中建议的连接限制的修复RFC 2626 https://www.w3.org/Protocols/rfc2616/rfc2616-sec8.html#sec8.1.4许多 Web 开发人员通过使用 CDN 和类似的东西来避免这种情况。目前,大多数实现使用 100 个通道的最大限制,因此据我所知,可以通过单个连接同时下载。
通常打开和关闭连接需要一些时间和服务器资源,服务器必须处理的打开连接越多,系统可能遭受的损失就越大。尽管几乎没有任何流量的开放连接对于大多数服务器来说并不是一个大问题。虽然连接创建通常被认为是成本高昂的部分,但通过使用持久连接,该因素现在转向了发出的请求数量,因此需要发送批量请求,而 HTTP 并不是真正为之而设计的。同样,正如整篇文章中提到的,如果可能的话,通过巧妙地利用缓存,大量请求可能永远不会到达服务器。这可能是减少同时请求数量的最佳优化策略之一,因为大量请求可能根本不会到达服务器。在这种情况下,最好的建议可能是查看哪些类型的资源被频繁请求,哪些请求占用了大量的处理能力,以及哪些请求可以通过利用缓存选项轻松得到响应。