【问题标题】:Microservice Composition Approaches微服务组合方法
【发布时间】:2015-10-12 23:01:45
【问题描述】:

我有一个关于微服务社区的问题。我将举一个教育领域的例子,但它适用于每个微服务架构。

假设我有 student-servicelicensing-service,其业务要求是学生数量受许可证限制。因此,每次创建学生时都必须进行许可检查。有多种类型的许可证,因此许可证的类型必须包含在操作中。

我的问题是您发现哪种方法在实践中更好:

  1. 构建调用这 2 个服务的复合服务
  2. 将学生服务与许可服务耦合,以便在调用 createStudent 时,学生服务会调用许可服务,并且只有在完成后才会创建学生
  3. 使用基于事件的架构

人们谈论微服务架构更像是一个图表而不是层次结构,而选项 1 有点将其变成了一个层次结构,在这个层次结构中你会得到越来越粗糙的组合。其他缺点是它会混淆客户端应该实际使用的服务,并且存在一些重复,因为复合 API 必须包含调用下游服务所需的所有参数。 它确实有一个很大的好处,因为它为您提供了一个自然的地方来进行故障处理、编排和处理一致性。

选项 2 似乎也有缺点:

  • 许可 API 必须泄漏到学生 API 中,以便您可以指定许可限制。

  • 它给学生服务带来了很多负担,因为它必须处理所有依赖服务的一致性

  • 由于在创建学生时需要更多服务做出反应,我可以看到依赖关系图迅速失控,并且服务必须处理这种复杂性,除了它自己用于管理学生的逻辑。

选项 3 在脱离天堂的同时,我真的认为这不会奏效,因为这一切都是由 UI 触发的,人们并不真正习惯“在这个新学生之前去做其他事情出现”的方法。

谢谢

【问题讨论】:

    标签: web-services architecture microservices


    【解决方案1】:

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

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

    通过这种方式,您可以将此中间件用于其他服务调用(例如课程服务),并且许可和学生的 API 更改可以独立完成,因为这些事情是真正独立的。只是碰巧许可使用的是学生人数,但这很容易改变。

    我不确定选项 3(基于事件的方法)如何提供帮助。但它可以解决其他问题。

    【讨论】:

    • 问题仍然存在,如果您将对这个中间件的调用由调用者负责。我将选项 1 理解为强制调用 create-student 遵守许可限制的一种方式。当然,如果许可服务和学生服务都可用,那么复合服务实际上不能强制执行任何操作。您会看到学生的创建和许可是完全独立的——考虑到业务需求,我不这样做,因此,选项 2 对我来说很有意义。我认为,归结为您是强调业务需求还是灵活性。
    • @schaueho - 我想你可以简单地阻止来自外部世界的学生服务调用,如果它们不是通过许可中间件来的。
    【解决方案2】:

    恕我直言,我会选择选项 2。有几件事需要考虑。如果您完全购买 SOA 以及微服务,那么您不能在每次服务需要联系另一个服务时退缩。对此感到舒服......记住这就是重点。我真正喜欢选项 2 的地方在于,在许可证服务请求成功之前不会发送成功的学生服务响应。将许可服务视为任何其他外部服务,您可以将许可服务包装在可由许可服务 JAR 发布的客户端对象中。

    • 许可 API 必须泄漏到学生 API 中,以便您可以指定许可限制。

    是的,将使用许可服务 API。您可以将其称为泄漏(必须有人使用它)或封装,以便请求学生服务的客户端无需担心许可问题。

    • 它给学生服务带来了很多负担,因为它必须处理所有依赖服务的一致性

    某些服务必须承担这个负担。但我会有机地管理它。我们正在谈论一项服务需要另一项服务。如果这种情况增长并变得具体麻烦,那么可以进行重构。如果学生服务所需的服务数量增加,我认为它可以优雅地重构,并且学生服务可能成为复合服务,如果需要,可以将独立使用的服务组合并为新服务。但是,如果学生服务使用的依赖服务列表仅由学生服务使用,那么我不知道是否值得将它们分组到自己的服务中。我认为您可以将其视为封装和所有权,而不是负担和泄漏......其中学生服务是该负担的所有者,因此它不需要泄漏给其他客户/服务。

    • 由于在创建学生时需要更多服务做出反应,我可以看到依赖关系图迅速失控,并且服务必须处理这种复杂性,除了它自己用于管理学生的逻辑。

    替代方案是各种复合服务。就像我对上一个要点的回应一样,如果它作为一个真正的问题浮出水面,就可以优雅地解决。

    如果强制,您的每个选项都可以变成可行的解决方案。我正在为选项 2 提出一个固执己见的案例。

    【讨论】:

      【解决方案3】:

      我知道这个问题已在不久前提出,但我想我有话要说,在这里可能很有价值。
      首先,您的方法将取决于最终产品的整体尺寸。我倾向于使用经验法则:如果单个微服务之间的依赖关系过多,我倾向于使用可以简化并可能删除这些依赖关系的东西。我不想以服务蜘蛛网告终!值得关注的是消息队列,例如 RabbitMQ
      但是,如果我只有几个相互通信的服务,我会让它们直接相互调用,因为任何替代解决方案在简化架构的同时增加了一些计算和基础设施开销。

      无论您决定采用哪种方法,在设计服务时都要牢记Hexagonal architecture!当您决定从一种解决方案迁移到另一种解决方案时,这将为您省去麻烦。我倾向于将我的 DAO 设计为“适配器”,因此调用服务 A 的 DAO 将直接调用它或通过消息队列调用它,而与业务逻辑无关。当我需要更改它时,我可以将这个 DAO 更改为另一个,而无需触及任何业务逻辑(最终,业务逻辑并不关心它如何获取数据)。六边形架构非常适合微服务、TDD 和黑盒测试。

      【讨论】:

        【解决方案4】:

        选项 1 和 2 会产生紧密耦合,应尽可能避免这种情况,因为您希望服务是独立的。那么问题就变成了:

        我们如何使用基于事件的架构来做到这一点?

        1. 使用事件来跟踪学生服务中许可服务的许可信息,实际上是数据复制。这里的缺点是:你只有最终的一致性,因为数据复制是异步的。

        2. 使用异步事件触发最终触发学生创建的事件链。从您的问题来看,您似乎已经有了这个想法,但是在处理 UI 时遇到了问题。您在这里有两种可能的选择:等待学生创建(或失败)事件并有少量超时,或者(事件更好),让您的系统完全响应(对 UI 使用服务器-客户端推送机制)。

        【讨论】:

          【解决方案5】:

          我推荐选项 3。您必须在可用性和一致性之间做出选择——而可用性是微服务架构中最需要的。

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

          当 UI 调用您的 API 网关来创建学生时,网关会简单地调用 Student 服务进行创建并返回 202 Accepted 或 200 OK 响应,而无需等待学生获得正确的许可。当学生通过异步通信(例如通过长轮询或网络套接字)获得许可时,UI 可以通知用户。

          如果许可服务出现故障或速度缓慢,则只有许可会受到影响。学生服务仍然可用,并将继续成功处理请求。一旦许可证服务再次健康,服务总线将从队列中推送任何待处理的“StudentCreated”事件(最终一致性)。

          这种方法还鼓励扩展。未来添加的新微服务可以订阅这些事件,而无需对学生或许可微服务进行任何更改(解耦)。

          使用选项 1 或选项 2,您不会获得任何这些好处,并且您的许多微服务将由于一个不健康的微服务而停止工作。

          【讨论】:

            猜你喜欢
            • 2022-08-19
            • 2017-10-06
            • 1970-01-01
            • 2018-10-17
            • 2020-01-09
            • 2016-04-15
            • 2020-01-30
            • 2015-08-05
            • 1970-01-01
            相关资源
            最近更新 更多