数据二分法:重新思考数据与服务之间的关系

大家好! 我们有好消息,OTUS 将于 XNUMX 月再次推出该课程 《软件架构师》,我们通常会与您分享有用的材料。

数据二分法:重新思考数据与服务之间的关系

如果您在没有任何背景的情况下遇到了整个微服务问题,您会觉得这有点奇怪,这是情有可原的。 将应用程序拆分为通过网络连接的片段必然意味着向最终的分布式系统添加复杂的容错模式。

尽管这种方法涉及将其分解为许多独立的服务,但最终目标不仅仅是让这些服务在不同的计算机上运行。 我们这里讲的是与外界的交互,本质上也是分布式的。 不是在技术意义上,而是在一个由许多人、团队、程序组成的生态系统的意义上,每个部分都需要以某种方式完成其工作。

例如,公司是分布式系统的集合,这些系统共同致力于实现某些目标。 几十年来,我们一直忽视这一事实,试图通过 FTP 文件传输或使用企业集成工具来实现统一,同时专注于我们自己孤立的目标。 但随着服务的出现,一切都改变了。 服务帮助我们超越视野,看到一个相互依赖、协同工作的项目的世界。 然而,为了成功工作,有必要认识和设计两个根本不同的世界:外部世界,我们生活在许多其他服务的生态系统中;以及我们个人的内部世界,我们独自统治。

数据二分法:重新思考数据与服务之间的关系

这个分布式世界与我们成长和习惯的世界不同。 构建传统整体架构的原则经不起批评。 因此,正确使用这些系统不仅仅是创建一个很酷的白板图或一个很酷的概念证明。 关键是要确保这样的系统能够长期成功运行。 幸运的是,这些服务已经存在相当长一段时间了,尽管它们看起来有所不同。 SOA 课程 即使使用 Docker、Kubernetes 和略显破旧的时髦胡须,仍然具有相关性。

因此,今天我们将了解规则是如何变化的,为什么我们需要重新思考我们处理服务和它们相互传递的数据的方式,以及为什么我们需要完全不同的工具来做到这一点。

封装并不总是你的朋友

微服务可以彼此独立运行。 正是这种财产赋予了他们最大的价值。 同样的属性允许服务扩展和增长。 与其说是扩展到千万亿用户或拍字节数据(尽管这些也可以有所帮助),不如说是随着团队和组织不断发展而在人员方面进行扩展。

数据二分法:重新思考数据与服务之间的关系

然而,独立是一把双刃剑。 也就是说,服务本身可以轻松、自然地运行。 但是,如果在一个服务中实现的功能需要使用另一个服务,那么我们最终必须几乎同时对这两个服务进行更改。 在单体应用中,这很容易做到,您只需进行更改并将其发送到发布版本,但在同步独立服务的情况下,则会出现更多问题。 团队和发布周期之间的协调会破坏敏捷性。

数据二分法:重新思考数据与服务之间的关系

作为标准方法的一部分,他们只是试图避免烦人的端到端更改,在服务之间明确划分功能。 单点登录服务就是一个很好的例子。 它具有明确定义的角色,使其与其他服务区分开来。 这种明显的分离意味着,在周围服务需求快速变化的世界中,单点登录服务不太可能改变。 它存在于严格限制的环境中。

数据二分法:重新思考数据与服务之间的关系

问题在于,在现实世界中,业务服务无法始终保持同样纯粹的角色分离。 例如,相同的业务服务可以更大程度地利用来自其他类似服务的数据。 如果您从事在线零售,那么处理订单流程、产品目录或用户信息将成为您许多服务的要求。 每个服务都需要访问这些数据才能运行。

数据二分法:重新思考数据与服务之间的关系
大多数业务服务共享相同的数据流,因此它们的工作总是交织在一起。

因此,我们谈到了一个值得讨论的重要问题。 虽然服务对于基本上独立运行的基础设施组件来说效果很好,但大多数业务服务最终会更加紧密地交织在一起。

数据二分法

面向服务的方法可能已经存在,但它们仍然缺乏对如何在服务之间共享大量数据的深入了解。

主要问题是数据和服务是密不可分的。 一方面,封装鼓励我们隐藏数据,以便服务可以彼此分离,并促进其增长和进一步的变化。 另一方面,我们需要能够像任何其他数据一样自由地划分和征服共享数据。 关键是能够像在任何其他信息系统中一样自由地立即开始工作。

然而,信息系统与封装关系不大。 事实上,情况恰恰相反。 数据库竭尽全力提供对其存储的数据的访问。 它们配备了强大的声明式界面,允许您根据需要修改数据。 此类功能在初步研究阶段很重要,但对于管理不断发展的服务日益增长的复杂性而言并不重要。

数据二分法:重新思考数据与服务之间的关系

这里出现了一个困境。 矛盾。 二分法。 毕竟,信息系统是为了提供数据,而服务是为了隐藏。

这两种力量是根本性的。 他们支撑着我们的大部分工作,不断为我们构建的系统追求卓越而奋斗。

随着服务系统的成长和发展,我们在很多方面看到了数据二分法的后果。 要么服务接口将会增长,提供不断增加的功能范围,并开始看起来像一个非常奇特的本土数据库,要么我们会感到沮丧,并实现某种方法来检索或移动整个数据集从一个服务到另一个服务。

数据二分法:重新思考数据与服务之间的关系

反过来,创建看起来像精美的本土数据库的东西将导致一系列问题。 我们不会详细说明为什么它是危险的 共享数据库,我们只能说它代表着成本高昂的工程和运营 困难 对于尝试使用它的公司来说。

更糟糕的是,数据量放大了服务边界问题。 服务中共享的数据越多,接口就会变得越复杂,并且组合来自不同服务的数据集也就越困难。

提取和移动整个数据集的替代方法也有其问题。 解决这个问题的常见方法是简单地检索和存储整个数据集,然后将其本地存储在每个消费服务中。

数据二分法:重新思考数据与服务之间的关系

问题在于不同的服务对它们消耗的数据的解释不同。 这些数据随时可用。 它们在本地进行修改和处理。 很快,它们就不再与源中的数据有任何共同点。

数据二分法:重新思考数据与服务之间的关系
副本的可变性越大,数据随时间的变化就越大。

更糟糕的是,这些数据事后很难纠正(MDM的 这才是它真正可以发挥作用的地方)。 事实上,企业面临的一些棘手的技术问题源于应用程序之间不断增加的不同数据。

为了找到这个问题的解决方案,我们需要对共享数据进行不同的思考。 它们必须成为我们构建的架构中的一流对象。 帕特·海兰德 将此类数据称为“外部”,这是一个非常重要的特征。 我们需要封装,这样我们就不会暴露服务的内部工作原理,但我们需要让服务能够轻松访问共享数据,以便它们能够正确地完成自己的工作。

数据二分法:重新思考数据与服务之间的关系

问题是这两种方法在今天都不再适用,因为服务接口、消息传递和共享数据库都没有提供处理外部数据的良好解决方案。 服务接口不太适合任何规模的数据交换。 消息传递会移动数据但不存储其历史记录,因此数据会随着时间的推移而损坏。 共享数据库过于关注某一点,从而阻碍了进展。 我们不可避免地陷入数据失败的循环:

数据二分法:重新思考数据与服务之间的关系
数据失效循环

Streams:一种去中心化的数据和服务方法

理想情况下,我们需要改变服务处理共享数据的方式。 此时,任何一种方法都面临着前面提到的二分法,因为没有魔法粉末可以撒在它上面让它消失。 不过,我们可以重新思考问题并达成妥协。

这种妥协涉及一定程度的集中化。 我们可以使用分布式日志机制,因为它提供了可靠、可扩展的流。 我们现在希望服务能够加入并在这些共享线程上进行操作,但我们希望避免执行此处理的复杂的集中式上帝服务。 因此,最好的选择是将流处理构建到每个消费者服务中。 这样,服务将能够组合来自不同来源的数据集,并按照需要的方式使用它们。

实现这种方法的一种方法是使用流媒体平台。 有很多选择,但今天我们将看看 Kafka,因为使用它的状态流处理可以让我们有效地解决所提出的问题。

数据二分法:重新思考数据与服务之间的关系

使用分布式日志记录机制使我们能够遵循常见的路径并使用消息传递来处理 事件驱动架构。 这种方法被认为比请求-响应机制提供更好的扩展和分区,因为它将流的控制权交给了接收者而不是发送者。 然而,此生的一切都需要付出代价,而这里你需要一个经纪人。 但对于大型系统来说,这种权衡是值得的(对于普通的 Web 应用程序来说可能并非如此)。

如果代理负责分布式日志记录而不是传统的消息传递系统,则您可以利用其他功能。 传输几乎可以像分布式文件系统一样线性扩展。 数据可以在日志中保存相当长的时间,因此我们不仅得到消息交换,而且得到信息存储。 可扩展存储,无需担心可变共享状态。

然后,您可以使用有状态流处理将声明性数据库工具添加到消费服务中。 这是一个非常重要的想法。 虽然数据存储在所有服务都可以访问的共享流中,但服务所做的聚合和处理是私有的。 他们发现自己被孤立在一个严格限制的环境中。

数据二分法:重新思考数据与服务之间的关系
通过分离不可变状态流来消除数据二分法。 然后使用状态流处理将此功能添加到每个服务中。

因此,如果您的服务需要处理订单、产品目录、仓库,它将具有完全访问权限:只有您才能决定要合并哪些数据、在哪里处理数据以及随着时间的推移如何更改数据。 尽管数据是共享的,但数据的使用是完全分散的。 它是在每项服务中产生的,在一个一切都按照您的规则进行的世界中。

数据二分法:重新思考数据与服务之间的关系
共享数据而不损害其完整性。 将功能(而不是源)封装在每个需要它的服务中。

碰巧数据需要集体移动。 有时,服务需要所选数据库引擎中的本地历史数据集。 诀窍在于,您可以保证在必要时可以通过访问分布式日志记录机制从源恢复副本。 Kafka 中的连接器在这方面做得很好。

因此,今天讨论的方法有几个优点:

  • 数据以公共流的形式使用,可以长期存储在日志中,并且在每个单独的上下文中硬连线处理公共数据的机制,这使得服务可以轻松快速地工作。 这样就可以平衡数据的二分性。
  • 来自不同服务的数据可以轻松组合成集合。 这简化了与共享数据的交互,并且无需在数据库中维护本地数据集。
  • 有状态流处理仅缓存数据,真实来源仍然是一般日志,因此随着时间的推移数据损坏的问题并不那么严重。
  • 服务的核心是数据驱动的,这意味着尽管数据量不断增加,服务仍然可以快速响应业务事件。
  • 可扩展性问题落在经纪人身上,而不是服务身上。 这显着降低了编写服务的复杂性,因为无需考虑可扩展性。
  • 添加新服务不需要更改旧服务,因此连接新服务变得更加容易。

正如您所看到的,这不仅仅是 REST。 我们收到了一套工具,可让您以分散的方式处理共享数据。

今天的文章并未涵盖所有方面。 我们仍然需要弄清楚如何在请求响应范式和事件驱动范式之间取得平衡。 但我们下次会处理这个问题。 有些主题您需要更好地了解,例如,为什么有状态流处理如此出色。 我们将在第三篇文章中讨论这个问题。 如果我们诉诸它们,我们还可以利用其他强大的结构,例如, 恰好一次处理。 它是分布式业务系统的游戏规则改变者,因为它为分布式业务系统提供了事务保证 XA 以可扩展的形式。 这将在第四篇文章中讨论。 最后,我们需要回顾这些原则的实施细节。

数据二分法:重新思考数据与服务之间的关系

但现在,请记住这一点:数据二分法是我们在构建业务服务时面临的一种力量。 我们必须记住这一点。 诀窍是彻底改变一切,开始将共享数据视为一流对象。 有状态流处理为此提供了独特的折衷方案。 它避免了阻碍进步的中心化“上帝组件”。 此外,它确保了数据流管道的敏捷性、可扩展性和弹性,并将其添加到每项服务中。 因此,我们可以专注于任何服务都可以连接并处理其数据的一般意识流。 这使得服务更具可扩展性、可互换性和自主性。 因此,它们不仅在白板和假设检验上看起来不错,而且还可以工作和发展数十年。

了解有关课程的更多信息。

来源: habr.com

添加评论