【问题标题】:How to deal with shared state in a micro-service architecture?如何处理微服务架构中的共享状态?
【发布时间】:2015-04-30 07:41:54
【问题描述】:

在我们公司,我们正在从庞大的单体应用程序过渡到微服务架构。这一决定的主要技术驱动因素是需要能够独立扩展服务和开发的可扩展性 - 我们有 10 个 Scrum 团队在不同的项目(或“微服务”)中工作。

过渡过程很顺利,我们已经开始受益于这种新技术和组织结构的优势。现在,另一方面,我们正在努力解决一个主要的痛点:如何管理这些微服务之间的依赖关系的“状态”

让我们举个例子:其中一个微服务处理用户和注册。该服务(我们称之为 X)负责维护身份信息,因此是用户“id”的主要提供者。其余的微服务对这个有很强的依赖。例如,有一些服务负责用户配置文件信息 (A)、用户权限 (B)、用户组 (C) 等,它们依赖于这些用户 ID,因此需要在这些服务之间维护一些数据同步(即服务 A 不应该有未在服务 X 中注册的用户 ID 的信息)。我们目前通过使用 RabbitMQ 通知状态更改(例如新注册)来保持这种同步。

您可以想象,有许多 X:许多“主要”服务以及它们之间的许多更复杂的依赖关系。

主要问题出现在管理不同的开发/测试环境时。每个团队(因此,每个服务)都需要经过几个环境才能使一些代码生效:持续集成、团队集成、验收测试和实时环境。

显然,我们需要在所有这些环境中运行的所有服务来检查系统是否作为一个整体运行。现在,这意味着为了测试依赖服务(A,B,C,...),我们不仅要依赖服务 X,还要依赖它的状态。 因此,我们需要以某种方式维护系统完整性并存储全局一致的状态

我们目前的方法是从实时环境中获取所有数据库的快照,进行一些转换以缩小和保护数据隐私,并在特定环境中进行测试之前将其传播到所有环境。这显然是组织和计算资源方面的巨大开销:我们有十个持续集成环境、十个集成环境和一个验收测试环境,所有这些环境都需要使用来自实时和最新版本代码的共享数据“刷新”频繁地。

我们正在努力寻找一种更好的方法来缓解这种痛苦。目前我们正在评估两个选项:

  1. 为所有这些服务使用类似 docker 的容器
  2. 每个服务有两个版本(一个用于开发该服务,另一个用作沙盒,供其他团队在开发和集成测试中使用)

这些解决方案都不能减轻服务之间共享数据的痛苦。我们想知道其他一些公司/开发人员是如何解决这个问题的,因为我们认为这在微服务架构中一定很常见。

你们做得怎么样?你也有这个问题吗?有什么推荐吗?

抱歉解释太长,非常感谢!

【问题讨论】:

  • 当您说存储一个全局和连贯状态时,您是指与实时系统相同的状态还是某个合成状态?正如我所见,您有多个级别的集成环境,每个级别都专注于特定的微服务。
  • 理想情况下,微服务不应依赖于其他服务(以及其状态,与@Eugene 所述相同),而应仅依赖于定义明确的通信合同。这种分解的主要好处是独立交付。每个服务都可以独立部署,这适用于任何环境级别(每个团队、分段或实时)。从这个角度来看,每个环境都可以维护自己的合同实施。对于开发和团队环境,这可能是服务模拟器(在您的示例中为 X 模拟器)。它可能类似于您的 sandbox,我不确定。
  • 总结一下。这个想法是您不必维护所有环境的实时状态,因为它们中的大多数都不需要它。唯一的例外是登台。
  • 感谢您的 cmets,neleus。请阅读我对尤金回答的说明。
  • 所有团队就整个系统的“主”测试数据集达成一致 那么您是否考虑过仅将这些数据/状态交付给 staging 而不是所有团队?对于每个团队,设置覆盖定义明确的测试用例集的 X 部分模拟器将花费更少的精力。

标签: deployment architecture integration-testing microservices test-environments


【解决方案1】:

这次我从不同的角度阅读了你的问题,所以这里是“不同的意见”。我知道这可能为时已晚,但希望它有助于进一步发展。

看起来shared state 是错误解耦的结果。在“正确”的微服务架构中,所有微服务都必须在功能上而不是逻辑上隔离。我的意思是所有三个user profile information (A), user permissions (B), user groups (C) 在功能上看起来都一样,而且在功能上或多或少是连贯的。它们似乎是具有连贯存储的单个user microservice。我在这里看不到任何将它们解耦的原因(或者至少你没有告诉过它们)。

所以真正的问题与微服务隔离有关。理想情况下,每个微服务都可以作为完整的独立产品并提供明确定义的业务价值。在详细说明系统架构时,我们将其分解为微小的逻辑单元(在您的情况下为 A、B、C 等,甚至更小),然后定义功能上一致的子组。我不能告诉你如何做到这一点的确切规则,也许是一些例子。单元之间复杂的通信/依赖关系,在其普遍存在的语言中有许多常用术语,因此看起来这些单元属于同一个功能组,因此属于微服务。

因此,从您的示例来看,由于只有一个存储,因此您只能像以前那样管理其一致性。

顺便说一句,我想知道您解决问题的实际方法是什么?另外,如果您喜欢我的想法,请随意接受。

【讨论】:

    【解决方案2】:

    让我试着重新表述这个问题:

    演员:

    • X:用户 ID(帐户状态)
      • 提供服务以获取 ID(基于凭据)和帐户状态
    • A:用户配置文件
      • 使用 X 检查用户帐户的状态。商店名称以及帐户链接
      • 提供基于 ID 获取/编辑名称的服务
    • B:用户博客
      • 以同样的方式使用 X。当用户写一篇博文时,存储博客文章以及指向帐户的链接
      • 使用 A 根据用户名搜索博文
      • 提供服务获取/编辑基于ID的博客条目列表
      • 提供基于名称搜索博文的服务(依赖A)
    • C:移动应用
      • 将 X、A、B 的功能封装到移动应用中
      • 提供上述所有服务,依赖于与所有其他人的明确定义的通信合同(遵循@neleus 声明)

    要求:

    1. X、A、B、C 团队的工作需要解耦
    2. X、A、B、C 的集成环境需要使用最新功能进行更新(以便执行集成测试)
    3. X、A、B、C 的集成环境需要有“足够”的数据集(以便执行负载测试和发现边缘情况)

    遵循@eugene 的想法:对每个团队提供的每项服务进行模拟将允许 1) 和 2)

    • 团队的开发成本更高
    • 还维护了模拟和主要功能
    • 障碍在于您拥有一个整体系统(您还没有一组干净、定义良好/隔离的服务)

    建议的解决方案:

    如果有一个与一组主数据共享的环境来解决 3)?每个“交付的服务”(即在生产中运行)都是可用的。每个团队都可以选择他们将从这里使用的服务以及他们将在自己的环境中使用的服务

    我可以看到的一个直接缺点是数据的共享状态和一致性。

    让我们考虑针对主数据运行的自动化测试,例如:

    • B 更改名称(由 A 拥有)以使用其博客服务
      • 可能会破坏 A 或 C
    • A 更改帐户状态以处理某些权限方案
      • 可能会破坏 X,B
    • C 在同一帐户上更改所有内容
      • 打破所有其他

    主数据集很快就会变得不一致并失去其对上述要求 3) 的价值。

    因此,我们可以在共享主数据上添加一个“常规”层:任何人都可以读取完整数据集,但只能修改他们创建的对象?

    【讨论】:

      【解决方案3】:

      从我的角度来看,只有使用服务的对象才应该具有状态。让我们考虑您的示例:服务 X 负责用户 ID,服务 A 负责配置文件信息等。让我们假设用户 Y 具有一些安全令牌(例如可以通过使用其用户名和密码创建 - 应该是唯一)进入系统。然后,包含用户信息的客户端将安全令牌发送到服务 X。服务 X 包含有关链接到此类令牌的用户 ID 的信息。如果是新用户,服务 X 会创建新 ID 并存储它的令牌。然后服务 X 将 ID 返回给用户对象。用户对象通过提供用户 ID 向服务 A 询问用户配置文件。服务 A 获取 ID 并询问服务 X 是否存在该 ID。服务 X 发送肯定答案,然后服务 A 可以通过用户 ID 搜索配置文件信息或要求用户提供此类信息以创建它。相同的逻辑应该适用于 B 和 C 服务。他们必须互相交谈,但他们不需要知道用户状态。

      关于环境的几句话。我建议使用puppets。这是自动化服务部署过程的方法。我们正在使用 puppet 在不同的环境中部署服务。 puppet 脚本是可触及的,并允许灵活配置。

      【讨论】:

      • 感谢您的回答和对 Puppet 的建议,看起来真的很有趣。关于服务,让我再详细说明一下。按照已经设置的示例,服务 X 将负责 userIds(这意味着它以某种方式存储 对)。服务 A 负责用户配置文件,因此它需要存储 .
      • 正如您所提到的,在检索给定 userId 的 userProfile 时,服务 A 与服务 X 通信以检查该帐户是否处于活动状态。使用这种方法,服务 X 和服务 A 在逻辑上是独立的,因此它们的代码可以独立发展和部署,这没有问题。
      • 为了进一步解释共享状态问题,我们假设我们的服务使用类似docker的容器。在创建新的测试/集成/等环境时,服务 X 和服务 A 的容器都可以部署到机器/环境中。现在,这两个服务所依赖的数据会发生什么?为了使整个系统保持一致,服务 X 存储的数据和服务 A 存储的数据需要保持一致。这意味着即使服务的代码可以独立部署,它们使用的数据也不能。
      • 这个问题的一个可能解决方案是所有团队都同意整个系统的“主”测试数据集(所有服务都应该有关于一组规范的 userIds 的信息)。当服务数量很多并且它们之间有很多依赖关系时,这确实很麻烦。
      • 另一种解决方案是我们目前正在使用的解决方案:为了保持这个系统的一致性,我们从实时环境中获取所有服务的数据并将其复制到适当的集成/测试环境中。此解决方案适用于少数环境和服务,但需要大量移动数据、花费大量时间且不可扩展。这就是为什么我们正在寻找更好的方法来做到这一点。我希望现在能更好地解释这个问题:)
      猜你喜欢
      • 2016-05-22
      • 1970-01-01
      • 1970-01-01
      • 2019-08-19
      • 2015-06-10
      • 2014-02-08
      • 2019-05-10
      • 2021-02-05
      • 2016-07-18
      相关资源
      最近更新 更多