微服务组合方法

7
我有一个问题想向微服务社区请教。我将以教育领域为例,但它适用于每个微服务架构。
假设我有 `student-service` 和 `licensing-service`,业务要求是学生人数受许可证限制。因此,每次创建学生时必须进行许可证检查。许可证有多种类型,因此操作中必须包括许可证的类型。
我的问题是你们在实践中发现哪种方法更好:
1. 构建调用两个服务的复合服务 2. 将 `student-service` 与 `licensing-service` 耦合,因此当调用 `createStudent` 时,`student-service` 将调用 `licensing-service`,只有当完成该调用后才会创建学生。 3. 使用基于事件的架构
人们谈论微服务架构更像图形而不是层次结构,`选项1` 使得我们越来越依赖逐渐粗糙的组合服务,并且还会导致混乱,使客户端真正应该使用哪个服务变得不清楚,因为组合API必须包含调用下游服务所需的所有参数。它确实有一个巨大的好处,因为它为您提供了处理失败、编排和处理一致性的自然位置。
`选项2` 似乎也有缺点:
- 许可证API的信息会泄露到学生API中,以便您可以指定许可证限制。 - 它给 `student-service` 带来了很大的负担,因为它必须跨所有依赖服务处理一致性。 - 随着越来越多的服务需要在创建学生时响应,我可以看到依赖图很快变得失控,服务还必须处理除了自己管理学生的逻辑之外的复杂性。
`选项3` 虽然是解耦的天堂,但我认为它不太可行,因为这都是从 UI 触发的,人们并不习惯“去做其他事情,直到这个新学生出现”的方法。
谢谢。
5个回答

3
选项1和2会产生紧密耦合,应尽量避免,因为您希望服务是独立的。所以问题就变成了:
如何在基于事件的架构中实现呢?
1.使用事件来跟踪来自许可证服务的许可信息,实际上是数据复制到学生服务。这里的缺点是:由于数据复制是异步的,你只有最终一致性。
2.使用异步事件触发事件链,最终触发学生创建。从你的问题来看,你已经有了想法,但在处理UI方面存在问题。这里你有两个选择:等待学生创建(或失败)事件并设置一个小的超时时间,或者更好地,使系统完全反应(使用服务器-客户端推送机制进行UI)。

2

应用程序许可和创建学生是正交的,因此选项2没有意义。

选项1更明智,但我会尽量不建立另一个服务。相反,我会尝试通过许可中间件“过滤”对学生服务的调用。

这样,您可以将此中间件用于其他服务调用(例如课程服务),并且许可和学生API的更改可以独立完成,因为这些事情确实是独立的。只是许可使用的学生数量可能会轻松更改。

我不确定选项3,基于事件的方法如何在这里有所帮助。它可以解决其他问题。


问题在于是否将此中间件的调用留给调用者的责任。我理解选项1是强制调用create-student遵守许可限制的一种方式。当然,如果许可证和学生服务都可用,组合服务实际上无法强制执行任何内容。 您认为学生创建和许可证完全独立 - 鉴于业务要求,我不这样认为,因此,选项2对我来说非常有意义。我认为,关键在于强调业务需求还是灵活性。 - schaueho
@schaueho - 我认为你可以简单地阻止来自外部世界的对学生服务的调用,如果它们没有通过许可中间件进行。 - Pol

2
在我看来,我会选择第二个选项。有几个需要考虑的问题。如果你完全采用SOA和微服务,那么每次一个服务需要联系另一个服务时,你不能退缩。要适应这一点……记住这是重点。我真正喜欢的是,在许可证服务请求成功之前,不会发送成功的学生服务响应。将许可证服务视为任何其他外部服务,你可以将许可证服务包装在客户端对象中,并发布许可证服务JAR。
许可API必须泄漏到学生API中,以便你可以指定许可限制。
是的,将使用许可证服务API。你可以称其为泄漏(必须有人使用它),或者封装,以便请求学生服务的客户端无需担心许可证。
它使学生服务承担了很大的负担,因为它必须处理所有相关服务的一致性。
某些服务必须承担这种负担。但是我会有机地管理它。我们正在谈论一个服务需要另一个服务。如果这变得越来越困难,则可以进行重构。如果学生服务所需的服务数量增加,则可以进行优雅的重构,也许学生服务成为组合服务,并且独立使用的服务组可能会根据需要合并为新的服务。但是,如果学生服务使用的依赖服务列表仅由学生服务使用,则我不知道是否值得将它们分组到自己的服务中。我认为,与其看作负担和泄漏,不如将其视为封装和所有权……学生服务是该负担的所有者,因此无需泄漏给其他客户端/服务。
随着更多服务需要在创建学生时做出反应,我可以看到依赖关系图很快变得失控,并且服务必须处理除了管理学生的逻辑之外的复杂性。
另一种选择是各种组合服务。像我对上一个问题的回答一样,如果这成为真正的问题,那么可以优雅地解决它。
如果被迫,你的每个选项都可以变成可行的解决方案。我对第二个选项有自己的观点。

1

我建议选择第三个选项。在微服务架构中,可用性往往是最受欢迎的选择,而一致性和可用性之间必须做出选择。

您的“学生”聚合应该有一个“许可状态”属性。当创建学生时,其许可状态设置为“未验证”,并发布“StudentCreated”事件。然后,许可证服务应对此事件作出反应,并尝试为该学生保留许可证。然后相应地发布“已保留”或“已拒绝”事件。学生服务将通过订阅这些事件来更新学生的状态。

当UI调用API网关创建学生时,网关将简单地调用学生服务进行创建,并返回202 Accepted或200 OK响应,无需等待学生被适当授权。UI可以通过异步通信(例如长轮询或Web套接字)在学生获得许可证时通知用户。

如果许可证服务停机或缓慢,只会影响许可证颁发。学生服务仍将可用,并将继续成功处理请求。一旦许可证服务再次正常运行,服务总线将从队列中推送任何待处理的“StudentCreated”事件(最终一致性)。

这种方法也鼓励扩展。将来添加的新微服务可以订阅这些事件,而无需对学生或许可证微服务进行任何更改(解耦)。
使用选项1或选项2,您将无法获得这些好处,并且您的许多微服务将因一个不健康的微服务而停止工作。

0

我知道这个问题很久以前就被问过了,但我认为我有一些话可以在这里说出来。
首先,你的方法将取决于最终产品的整体大小。我倾向于遵循一个经验法则:如果微服务之间存在太多依赖关系,我会使用某些能够简化并可能消除这些依赖关系的解决方案。我不想最终得到一个像蜘蛛网一样的服务集群!在这里可以看一下消息队列,例如 RabbitMQ
然而,如果我只有几个互相通信的服务,我将直接让它们相互调用,因为任何替代方案虽然可以简化架构,但会增加一些计算和基础设施开销。

无论你决定采用哪种方法,都要以六边形架构为基础来设计你的服务!这样当你决定从一种解决方案迁移到另一种时,就能避免麻烦。我倾向于将我的DAO设计为“适配器”,因此调用A服务的DAO可以直接调用它,也可以通过消息队列调用它,与业务逻辑无关。当我需要更改时,只需将该DAO更换为另一个,而无需触及任何业务逻辑(归根结底,业务逻辑并不关心数据是如何获取的)。六边形架构非常适合微服务、TDD和黑盒测试。

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