跨微服务查询/分页

Posted

技术标签:

【中文标题】跨微服务查询/分页【英文标题】:Querying / Pagination Across Microservices 【发布时间】:2017-04-15 12:16:20 【问题描述】:

我们的商店最近开始采用 SOA 方法进行应用程序开发。我们看到了关注点分离、可重用性和 SOA/微服务的其他好处的一些巨大好处。

但是,我们遇到的一个项是跨服务聚合、过滤和分页结果。让我用一个场景来描述这个问题。

假设我们有 3 个服务:

    PersonService - 存储人员信息(姓名、地址等) ItemService - 存储有关可购买物品的信息。 PaymentService - 存储有关人们为不同项目进行的付款的信息。

现在,假设我们要构建一个报告/管理工具,可以汇总显示/报告多个服务。例如,我们想要显示一个分页的付款列表,以及每次付款所针对的人员和项目。这非常简单:获取付款列表,然后查询 PersonService 和 ItemService 以获取相应的 Person 和 Item 记录。

但是,当我们想要过滤该数据时,问题就出现了:例如,显示购买了“汽车”项目的名字为“鲍勃”的人支付的分页列表。这使事情变得更加复杂,因为我们需要过滤来自 3 个不同服务的结果,而不知道每个服务将返回多少结果。

从性能的角度来看,一遍又一遍地查询所有服务以缩小结果范围会很昂贵,因此我一直在研究更好的解决方案。但是,我找不到这个问题的具体解决方案(或者至少是“最佳实践”)。在单体应用程序中,我们只需在不同的表中使用 SQL 连接。我在弄清楚如何/是否在服务中可能出现类似的事情时遇到了很多麻烦。

我向社区提出的问题是:您的方法是什么?我考虑过的事情:

    使用某种搜索索引(ElasticsearchSolr)包含所有服务的所有数据(通过服务推出的事件更新),然后查询搜索结果索引。 尝试了解 GraphQLNeo4j 等项目如何帮助我们解决这些问题。

【问题讨论】:

Chris Richardson 对每个服务的数据库和服务之间的共享数据库有一些见解:microservices.io/patterns/data/database-per-service.html 嗨,迈克。你是如何解决这个问题的?我也遇到了类似的情况。我有 ES 作为数据存储。 @Richa 我们真的没有。我们仍然对不同的服务进行 REST 调用来检索我们想要的数据。然而,我们正在朝着事件溯源模式发展,在这种模式下,任意数量的服务都可以侦听数据并存储它们需要的数据。因此,所有更新都通过事件总线,服务复制其业务流程所需的数据。您可以将其视为类似于 NoSQL 方法(查看 Firebase 建议如何存储可在多个领域重复使用的对象)。 @Richa:你现在有机会解决这个问题吗?我也遇到了您描述的相同情况,但找不到具体的解决方案。我很早就在 SOF 上发布了我的问题,但还没有得到任何回应。 @Mike:你能详细说明一下事件溯源模式吗?谢谢 【参考方案1】:

我坚持Sam Newman,他在他的书的第 4 章“共享数据库”中说:

还记得我们谈到优秀微服务背后的核心原则吗?强内聚和松耦合——通过数据库集成,我们失去了这两样东西。数据库集成使服务共享数据变得非常容易,但对共享行为没有任何作用。我们的内部代表通过电线暴露给我们的消费者,并且很难避免做出重大更改,这不可避免地会导致对任何更改的恐惧。 (几乎)不惜一切代价避免。

这是我在 Content-Management-Systems 咒骂时要表达的观点。

在我看来,微服务是自治的,如果它共享或消费共享的东西,它就不可能是自治的。我在这里提出的唯一例外是域对象,它们代表对业务模型的共同理解,并且必须仅用于微服务之间的通信。

ER 或面向聚合的数据库(分为基于文档或基于图形)是否更适合需求取决于微服务本身。 有趣的是,通过松散耦合和自治,您可以做到这一点!

如果 PaymentService 共享“为 A 支付多少付款”的行为 他需要了解人 A 才能完成这一点。但是他所知道的关于人 A 的一切都必须来自 PersonService,可能在运行时(PaymentService 可能只存储一个 id)或基于事件(PaymentService 将它需要的数据存储给域对象用户,触发并提供更新的内容由 PersonService)。 PaymentService 本身不共享用户本身。

【讨论】:

【参考方案2】:

除了 SQL 之外,将这些数据存储在 elasticsearch/solr/cognitivesearch 类型的服务中可以帮助解决其中的一些问题。

在你给定的例子中,

在搜索索引中(elasticsearch/solr/cognitivesearch)person 对象将有一个名为 "items" 的属性,其中包含该人支付的项目列表。

这样,您可以跨对象进行过滤,获取按人员的任何属性排序的分页列表。您可以在其他文档中添加类似信息,以更好地满足您的业务需求。

使用 GraphDatabase 似乎可以解决 10000 英尺的问题,但在大规模操作时会遇到分页问题。 GraphDatabases do not do pagination well(无论如何它们都必须访问所有节点,即使您需要分页列表也是如此)并且会导致超时/性能问题。

【讨论】:

【参考方案3】:

您可以使用复制表。 所有数据库都具有复制功能 如果您有具有人员表的人员服务和具有付款表的付款服务,则创建具有人员和付款表的报告服务,它们由复制功能填充。

【讨论】:

以上是关于跨微服务查询/分页的主要内容,如果未能解决你的问题,请参考以下文章

.Net 核心微服务查询来自许多服务

微服务实践——Docker与服务发现

3.16 Go微服务实战(微服务理论) --- Go语言基于ES-CQRS的微服务实践

3.16 Go微服务实战(微服务理论) --- Go语言基于ES-CQRS的微服务实践

微服务实战:选择微服务部署策略

Serverless 微服务实践-移动应用包分发服务