是否有将多个 API 调用合并为一个 HTTP 请求的标准方法?
Is there standard way of making multiple API calls combined into one HTTP request?
在设计 rest API 时,我有时会遇到处理批处理操作(例如一次删除或更新多个实体)以减少许多 tcp 客户端连接的开销的挑战。在特定情况下,问题通常通过为特定操作添加自定义 api 方法来解决(例如 POST /files/batchDelete
在请求正文中接受 ids
),从其余的角度来看这看起来并不漂亮 api 设计原则,但完成工作。
但对我来说,问题的一般解决方案仍然可取。最近我发现 Google Cloud Storage JSON API batching documentation 这对我来说看起来是非常通用的解决方案。我的意思是类似的格式可以用于任何 http api,而不仅仅是 google 云存储。所以我的问题是 - 有人知道将多个 API 调用合并到一个 HTTP 请求中的通用标准(标准或其草案、指南、社区努力等)吗?
我知道 http/2 的功能,其中包括对 http 请求使用单个 tcp 连接,但我的问题是针对应用程序级别的。在我看来这仍然是有道理的,因为尽管能够使用 http/2 在应用程序级别采用它似乎是保证任何客户端包括 http/1 这是目前最常用的 http 版本的唯一方法。
TL;DR
- REST 和 HTTP 都不适合批处理操作。
- 通常,作为 REST 约束之一的缓存不是可选的,而是强制性的,它会以某种形式阻止批处理。
- 不将要更新或删除的数据作为自己的资源公开,而是作为单个资源中的数据元素公开,例如 HTML 页面中的数据 table 可能会有所帮助。在这里更新或删除全部或部分条目应该很简单。
- 如果系统总体上是 write-intensive,最好考虑其他解决方案,例如将数据库直接暴露给那些客户端,以节省更高级别的间接性和复杂性。
- 缓存的使用可以避免服务器上的大量工作负载,甚至可以避免不必要的连接
首先,REST 和 HTTP 都不是批处理操作的理想选择。正如 Jim Webber 指出的 the application domain of HTTP is the transfer of documents over the Web。这就是 HTTP 所做的,也是它擅长的。然而,我们得出的任何业务规则都只是文档管理的副作用,我们必须想出解决方案,将文档管理的副作用转化为有用的东西。
由于 REST 只是可浏览 Web 中使用的概念的概括,因此适用于 Web 开发的相同概念也以某种形式适用于 REST 开发也就不足为奇了。因此,像在 REST 中应该如何完成某些事情这样的问题通常会围绕回答在 Web 上应该如何完成某些事情来解决。
如前所述,HTTP 在批处理操作方面并不理想。当然,一个 GET 请求可能会检索多个结果,但实际上您会获得一个包含指向更多资源的链接的响应。根据 HTTP specification,资源的创建必须用指向新创建的资源的 Location
header 来指示。 POST
被定义为一种通用方法,允许根据 server-specific 语义执行任务。所以你基本上可以用它来一次创建多个资源。但是,HTTP 规范显然不支持指示一次创建多个资源,因为 Location
header 每个响应可能只出现一次,并且在其中只定义一个 URI。那么服务器如何向服务器指示创建多个资源呢?
进一步表明 HTTP 不适合批处理的是 URI 必须引用单个资源。该资源可能会随着时间的推移而改变,尽管 URI 不可能同时指向多个资源。 URI 本身或多或少地被缓存用作键,缓存存储该 URI 的可缓存响应表示。由于 URI 可能只引用一个资源,缓存也将只存储该 URI 的一个资源的表示。如果对该 URI 执行了不安全的操作,缓存将使该 URI 的存储表示无效。如果 DELETE
操作本质上是不安全的,则将删除执行 DELETE
的 URI 的表示。如果您现在“重定向”DELETE
操作以一次删除多个后备资源,那么缓存应该如何注意到这一点?它只对调用的 URI 进行操作。因此,即使您通过 DELETE
一次性删除多个资源,缓存可能仍会为客户端提供过时的信息,因为它根本没有注意到删除,并且其新鲜度值仍会指示 fresh-enough状态。除非您默认禁用缓存,这在某种程度上违反了 REST's constraints 之一,或者将表示被认为足够新鲜的时间段减少到非常低的值,否则客户端可能会得到过时的信息。您当然可以对这些 URI 中的每一个执行不安全的操作,然后“清除”缓存,但在这种情况下,您可以对每个要批量删除自身的资源调用 DELETE
操作。
不过,如果您要删除的那批数据不是通过它们自己的资源明确捕获的,而是作为单个资源的数据,那么它会变得更容易一些。想一想网页上的 data-table,您有某些 form-elements,例如您可以单击以将条目标记为删除候选的复选框,然后在调用提交按钮后将相应的选定元素发送到执行删除这些项目的服务器。这里只有一个资源的状态被更新,因此可以对该资源 URI 执行简单的 POST
、PUT
甚至 PATCH
操作。这也适用于如前所述的缓存,因为只需更改一个资源,通过对该 URI 使用不安全操作将自动导致给定 URI 的任何存储表示失效。
上面提到的 form-elements 标记某些要删除的元素的用法取决于发布的 media-type。在 HTML 其 forms section specifies the available components and their affordances. An affordance is the knowledge what you can and should do with certain objects. I.e. a button or link may want to be pushed, a text field may expect numeric or alphanumeric input which further may be length limited and so on. Other media types, such as hal-forms, halform or ion 的情况下,尝试为基于 JSON 的符号提供表单表示和组件,但是r,对这种media-type的支持还是很有限的。
由于您担心的一个问题是与您服务的客户端连接数,我假设您有一个 write-intensive 场景,如 read-intensive 情况缓存可能会从你的服务器。 IE。 BBC 曾经报道过,他们可以通过为最近请求的资源引入一分钟的缓存间隔来大幅降低服务器的负载。这主要影响了他们的起始页和链接的文章,因为人们点击最新新闻的频率高于旧新闻。如前所述,如果每分钟收到几千个(如果不是几十万个)请求,他们可以显着减少实际到达服务器的请求数量,从而减轻服务器的巨大负载。
写入密集型 use-cases 但是不能像 read-intensive 那样利用缓存,因为缓存会经常失效,实际请求会转发到服务器进行处理。如果 API 或多或少用于执行 CRUD 操作,就像许多“REST” API 在现实中所做的那样,那么将数据库直接暴露给客户。几乎所有现代数据库供应商都附带了复杂的 user-right 管理选项,并允许创建可以向特定用户公开的视图。在这种情况下,它上面的“REST API”基本上只是增加了更高层次的间接性和复杂性。通过直接公开数据库,执行批量更新或删除根本不应该成为问题,因为通过相应的查询语言支持此类操作应该已经构建到数据库层中。
关于客户端创建的连接数:从 1.0 开始的 HTTP 允许通过 Connection: keep-alive
header directive. In HTTP/1.1 persistent connections are used by default if not explicitly requested to close via the respective Connection: close
header directive. HTTP/2 introduced full-duplex connections that allow many channels and therefore requests to reuse the same connections at the same time. This is more or less a fix for the connection limitation suggested in RFC 2626 重新使用连接,许多 Web 开发人员通过使用 CDN 和类似的东西避免了这一点。目前大多数实现使用 100 个通道的最大限制,因此通过单个连接 AFAIK 同时下载。
通常打开和关闭连接需要一些时间和服务器资源,服务器必须处理的打开连接越多,系统受到的影响就越大。尽管几乎没有任何流量的开放连接对于大多数服务器来说并不是一个大问题。虽然连接创建通常被认为是成本高昂的部分,但通过使用持久连接,该因素现在转向了发出的请求数量,因此发出 batch-requests 的请求并不是真正为 HTTP 而设计的。同样,如 post 中所述,通过智能利用缓存,如果可能的话,大量请求可能永远不会到达服务器。这可能是减少并发请求数量的最佳优化策略之一,因为可能有大量请求根本无法到达服务器。在这种情况下,最好的建议可能是查看经常请求哪种资源,哪些请求占用大量处理能力,哪些可以通过使用缓存选项轻松响应。
reduce overhead of many tcp client connections
如果这是问题的关键,解决这个问题的最简单方法是切换到 HTTP/2
在某种程度上,HTTP/2 完全符合您的要求。您打开 1 个连接,并使用该集合可以并行发送许多 HTTP 请求。与单个 HTTP 请求中的批处理不同,它对客户端和响应基本上是透明的,请求可以乱序处理。
最终在单个 HTTP 请求中批处理多个操作始终是网络攻击。
HTTP/2 广泛可用。如果 HTTP/1.1 仍然是最常用的版本(这可能是正确的,但差距正在缩小),这更多地与尚未为其设置的服务器有关,而不是客户端。
在设计 rest API 时,我有时会遇到处理批处理操作(例如一次删除或更新多个实体)以减少许多 tcp 客户端连接的开销的挑战。在特定情况下,问题通常通过为特定操作添加自定义 api 方法来解决(例如 POST /files/batchDelete
在请求正文中接受 ids
),从其余的角度来看这看起来并不漂亮 api 设计原则,但完成工作。
但对我来说,问题的一般解决方案仍然可取。最近我发现 Google Cloud Storage JSON API batching documentation 这对我来说看起来是非常通用的解决方案。我的意思是类似的格式可以用于任何 http api,而不仅仅是 google 云存储。所以我的问题是 - 有人知道将多个 API 调用合并到一个 HTTP 请求中的通用标准(标准或其草案、指南、社区努力等)吗?
我知道 http/2 的功能,其中包括对 http 请求使用单个 tcp 连接,但我的问题是针对应用程序级别的。在我看来这仍然是有道理的,因为尽管能够使用 http/2 在应用程序级别采用它似乎是保证任何客户端包括 http/1 这是目前最常用的 http 版本的唯一方法。
TL;DR
- REST 和 HTTP 都不适合批处理操作。
- 通常,作为 REST 约束之一的缓存不是可选的,而是强制性的,它会以某种形式阻止批处理。
- 不将要更新或删除的数据作为自己的资源公开,而是作为单个资源中的数据元素公开,例如 HTML 页面中的数据 table 可能会有所帮助。在这里更新或删除全部或部分条目应该很简单。
- 如果系统总体上是 write-intensive,最好考虑其他解决方案,例如将数据库直接暴露给那些客户端,以节省更高级别的间接性和复杂性。
- 缓存的使用可以避免服务器上的大量工作负载,甚至可以避免不必要的连接
首先,REST 和 HTTP 都不是批处理操作的理想选择。正如 Jim Webber 指出的 the application domain of HTTP is the transfer of documents over the Web。这就是 HTTP 所做的,也是它擅长的。然而,我们得出的任何业务规则都只是文档管理的副作用,我们必须想出解决方案,将文档管理的副作用转化为有用的东西。
由于 REST 只是可浏览 Web 中使用的概念的概括,因此适用于 Web 开发的相同概念也以某种形式适用于 REST 开发也就不足为奇了。因此,像在 REST 中应该如何完成某些事情这样的问题通常会围绕回答在 Web 上应该如何完成某些事情来解决。
如前所述,HTTP 在批处理操作方面并不理想。当然,一个 GET 请求可能会检索多个结果,但实际上您会获得一个包含指向更多资源的链接的响应。根据 HTTP specification,资源的创建必须用指向新创建的资源的 Location
header 来指示。 POST
被定义为一种通用方法,允许根据 server-specific 语义执行任务。所以你基本上可以用它来一次创建多个资源。但是,HTTP 规范显然不支持指示一次创建多个资源,因为 Location
header 每个响应可能只出现一次,并且在其中只定义一个 URI。那么服务器如何向服务器指示创建多个资源呢?
进一步表明 HTTP 不适合批处理的是 URI 必须引用单个资源。该资源可能会随着时间的推移而改变,尽管 URI 不可能同时指向多个资源。 URI 本身或多或少地被缓存用作键,缓存存储该 URI 的可缓存响应表示。由于 URI 可能只引用一个资源,缓存也将只存储该 URI 的一个资源的表示。如果对该 URI 执行了不安全的操作,缓存将使该 URI 的存储表示无效。如果 DELETE
操作本质上是不安全的,则将删除执行 DELETE
的 URI 的表示。如果您现在“重定向”DELETE
操作以一次删除多个后备资源,那么缓存应该如何注意到这一点?它只对调用的 URI 进行操作。因此,即使您通过 DELETE
一次性删除多个资源,缓存可能仍会为客户端提供过时的信息,因为它根本没有注意到删除,并且其新鲜度值仍会指示 fresh-enough状态。除非您默认禁用缓存,这在某种程度上违反了 REST's constraints 之一,或者将表示被认为足够新鲜的时间段减少到非常低的值,否则客户端可能会得到过时的信息。您当然可以对这些 URI 中的每一个执行不安全的操作,然后“清除”缓存,但在这种情况下,您可以对每个要批量删除自身的资源调用 DELETE
操作。
不过,如果您要删除的那批数据不是通过它们自己的资源明确捕获的,而是作为单个资源的数据,那么它会变得更容易一些。想一想网页上的 data-table,您有某些 form-elements,例如您可以单击以将条目标记为删除候选的复选框,然后在调用提交按钮后将相应的选定元素发送到执行删除这些项目的服务器。这里只有一个资源的状态被更新,因此可以对该资源 URI 执行简单的 POST
、PUT
甚至 PATCH
操作。这也适用于如前所述的缓存,因为只需更改一个资源,通过对该 URI 使用不安全操作将自动导致给定 URI 的任何存储表示失效。
上面提到的 form-elements 标记某些要删除的元素的用法取决于发布的 media-type。在 HTML 其 forms section specifies the available components and their affordances. An affordance is the knowledge what you can and should do with certain objects. I.e. a button or link may want to be pushed, a text field may expect numeric or alphanumeric input which further may be length limited and so on. Other media types, such as hal-forms, halform or ion 的情况下,尝试为基于 JSON 的符号提供表单表示和组件,但是r,对这种media-type的支持还是很有限的。
由于您担心的一个问题是与您服务的客户端连接数,我假设您有一个 write-intensive 场景,如 read-intensive 情况缓存可能会从你的服务器。 IE。 BBC 曾经报道过,他们可以通过为最近请求的资源引入一分钟的缓存间隔来大幅降低服务器的负载。这主要影响了他们的起始页和链接的文章,因为人们点击最新新闻的频率高于旧新闻。如前所述,如果每分钟收到几千个(如果不是几十万个)请求,他们可以显着减少实际到达服务器的请求数量,从而减轻服务器的巨大负载。
写入密集型 use-cases 但是不能像 read-intensive 那样利用缓存,因为缓存会经常失效,实际请求会转发到服务器进行处理。如果 API 或多或少用于执行 CRUD 操作,就像许多“REST” API 在现实中所做的那样,那么将数据库直接暴露给客户。几乎所有现代数据库供应商都附带了复杂的 user-right 管理选项,并允许创建可以向特定用户公开的视图。在这种情况下,它上面的“REST API”基本上只是增加了更高层次的间接性和复杂性。通过直接公开数据库,执行批量更新或删除根本不应该成为问题,因为通过相应的查询语言支持此类操作应该已经构建到数据库层中。
关于客户端创建的连接数:从 1.0 开始的 HTTP 允许通过 Connection: keep-alive
header directive. In HTTP/1.1 persistent connections are used by default if not explicitly requested to close via the respective Connection: close
header directive. HTTP/2 introduced full-duplex connections that allow many channels and therefore requests to reuse the same connections at the same time. This is more or less a fix for the connection limitation suggested in RFC 2626 重新使用连接,许多 Web 开发人员通过使用 CDN 和类似的东西避免了这一点。目前大多数实现使用 100 个通道的最大限制,因此通过单个连接 AFAIK 同时下载。
通常打开和关闭连接需要一些时间和服务器资源,服务器必须处理的打开连接越多,系统受到的影响就越大。尽管几乎没有任何流量的开放连接对于大多数服务器来说并不是一个大问题。虽然连接创建通常被认为是成本高昂的部分,但通过使用持久连接,该因素现在转向了发出的请求数量,因此发出 batch-requests 的请求并不是真正为 HTTP 而设计的。同样,如 post 中所述,通过智能利用缓存,如果可能的话,大量请求可能永远不会到达服务器。这可能是减少并发请求数量的最佳优化策略之一,因为可能有大量请求根本无法到达服务器。在这种情况下,最好的建议可能是查看经常请求哪种资源,哪些请求占用大量处理能力,哪些可以通过使用缓存选项轻松响应。
reduce overhead of many tcp client connections
如果这是问题的关键,解决这个问题的最简单方法是切换到 HTTP/2
在某种程度上,HTTP/2 完全符合您的要求。您打开 1 个连接,并使用该集合可以并行发送许多 HTTP 请求。与单个 HTTP 请求中的批处理不同,它对客户端和响应基本上是透明的,请求可以乱序处理。
最终在单个 HTTP 请求中批处理多个操作始终是网络攻击。
HTTP/2 广泛可用。如果 HTTP/1.1 仍然是最常用的版本(这可能是正确的,但差距正在缩小),这更多地与尚未为其设置的服务器有关,而不是客户端。