首页 > 解决方案 > 多头分页查询结果

问题描述

我们有一个用于对象之间关系建模的微服务。在具有基数约束(如 1-1、1-N、NN 等)的主对象和辅助对象之间定义关系。微服务提供创建关系、查找关系、获取辅助对象、获取主对象等 API。

查询 API“获取辅助对象”获取主对象并返回所有相关的辅助对象。由于相关的次要对象可能很大,因此对结果进行了分页。

我们有另一个微服务,它很好地利用了这个关系微服务来处理关系。该消费服务接受类似的分页选项,如页面索引和编号,并将其传递给关系服务,并将从关系服务获得的页面结果返回给调用应用程序。到目前为止一切都很好。

我们最近发现消费微服务与关系微服务有点啰嗦,因为它必须多次调用“获取辅助”API,因为必须获取多个主要对象的辅助对象。

所以我们想通过接受多个主要对象作为输入来使“获取辅助”API 成为批量 API。但后来我们陷入了分页的工作方式。API 将为每个主要对象返回相关的次要对象,但像之前一样将次要对象限制为页面大小。
这对于第一次调用来说似乎很好,但我们不确定这对于后续调用会如何表现。如果辅助对象的数量少于一个或多个主要对象的页面大小,那么后续调用的输入应该是什么。我需要再次传递那些主要对象吗?

这是我们正在寻找有关如何设计此批量 API 的建议的地方。欢迎任何意见。

标签: algorithmpaginationmicroservicesrelation

解决方案


基本上,您应该有一些方法来确保关系服务在接收分页请求时知道原始查询是什么。

关系服务处理此问题的一种简单且可维护的方法是通过以某种方式对请求的主要对象进行排序(即按 Id 字母顺序排序)来预处理请求,然后简单地遍历主要对象,将次要对象添加到响应中, 直到响应满。

客户端要做的最简单的事情是始终使用相同的批处理请求,并在请求中添加索引号或页面标记。

我会推荐一个页面标记,它提到最后看到的项目(例如,lastSeen=primaryId,secondaryId(你应该以某种方式混淆它以避免泄漏抽象))。然后,服务可以查看原始请求,并知道从哪里恢复遍历所有主要对象。

或者,您可以将足够的信息编码到页面令牌中,以便您可以从原始请求中重建您需要的任何内容。这允许您对后续请求的查询进行一些调整。(例如,如果客户端请求primaries A-Z,并且您在第一个响应中返回辅助对象A1 - J5,那么您可以将请求修改为J-Z; already seen J5,对其进行编码,以免泄露您的实现细节,并将其作为页面令牌。)然后,original request + page number客户端不使用 响应,而是简单地使用页面令牌进行响应。

无论哪种方式,关系服务的客户都不应该“弄清楚”下一页的请求应该是什么。分页应该只要求消费者增加一个数字或使用关系服务提供给它的页面令牌进行响应。

另一个考虑因素是您正在使用的数据库。例如,在 DynamoDB 中,为类似查询获取第 100 个项目的方法select * from secondaries where primaryId='ABC'要求您读取所有项目,直到第 100 个项目。如果您有一个 NoSQL 数据库,或者如果您认为将来可能会迁移到 NoSQL 数据库,您可能会发现页面令牌可以更轻松地维护您在结果集中的位置(与索引号)。

当我自己学习分页时,我发现这篇文章非常有帮助,我建议阅读它。它主要处理 UI 的分页问题,​​但基本原理是相同的。

TLDR:不要让消费者做任何工作。消费者应使用添加的索引号或页面令牌重复原始请求,或者消费者应发送仅包含页面令牌的请求。


推荐阅读