Monolitich 与分布式架构的权衡
Monolitich 与分布式架构的权衡
现在有很多关于分布式架构和事件源模型的讨论,特别是在云基础设施和 kubernetes 集群中。在过去的几年里,它获得了很大的动力,它可能会寻找一些人来解决他们的架构耦合和可伸缩性问题。过去几周我一直在阅读它,我想分享我对它的看法。
如果您正在考虑将单体应用程序分解为微服务,那么本文可能适合您。我将尝试剖析权衡并在某些实践下为某些情况捍卫整体方法。
定义
首先,什么是分布式架构?分布式系统有区别吗?什么是单体或微服务架构?
如果您已经熟悉这些术语,则可以跳过本节,如果没有,让我们定义这些概念以消除歧义。
此外,我在互联网上看到了围绕其中一些定义的分歧,所以我在最后一节中写下了我的参考资料。
分布式系统
首先介绍一个技术定义,然后总结一下。
“分布式系统是一种应用程序,它执行一组协议以协调网络上多个进程的动作,以便所有组件协同工作以执行单个或一小组相关任务。”
——华盛顿大学的定义。 [1]
因此,分布式系统只是一组通过远程访问协议连接以执行任务的机器。
客户端-服务器架构是分布式系统的一个示例。在最基本的 Web 应用程序中,您通过 HTTP 连接到服务器以获取网页。
单体架构
将单体架构想象成一个适用于整个应用程序的单个耦合代码库是很常见的 **** 生活在同一台机器上,但我们必须记住,如果我们严格要求,我们归类为“单体”的所有系统可能也是分布式系统。一个非静态的简单网页很可能会从另一台机器上运行的数据库中读取。所以我们不会仅仅根据系统中涉及的机器数量来定义它。
当所有代码都是单个部署单元时,体系结构就是整体的。
— 摘自 O'Reilly 的《软件架构基础》一书。 [2]
这是经典的定义,也是一个很好的定义,但让我们澄清一些事情。
系统的整体性取决于您确定它的范围。这意味着您的整个系统不能是单片的,但您的后端或前端可以。所以除了单体架构,我喜欢谈论 单片组件 .
我将这个陈述依赖于广泛的软件架构文献,因为每个人最终都在谈论系统单层的整体性。例如 Sam Newman 谈论微服务中的单一用户界面层。 [3]
因此,知道您可以谈论特定层的架构,即使您使用 REST API 将前端和后端解耦,也可能有一个单一的前端和一个连接到单一数据库的单一后端,因为这三个都是单一的部署单位。
分布式架构
好吧,考虑到单体架构的定义,我们可以用否定来定义分布式架构。
分布式架构是一组通过远程访问协议连接的部署单元。
— 摘自 O'Reilly 的《软件架构基础》一书。 [2]
这也适用于应用程序的任何层。例如:您的后端可能是一组在不同机器上运行的服务,每个服务都包含您的域逻辑的一个片段。或者,如果数据库通过分片、冗余系统和负载平衡器在多台机器上运行,则它可以是分布式的。
微服务
微服务架构是一种 SOA。 这意味着代码库是跨多个分布式服务构建的。微服务虽然具有在每个服务中包含自己封装的数据库的特殊性。它很大程度上基于 领域驱动设计 (DDD),因此服务是基于 DDD 的思想构建的 有界上下文。 该架构的核心思想之一是每个微服务都是可独立部署的,并且不与其他微服务耦合。
Microservices architecture
为什么需要微服务?
主要原因肯定是极端的可扩展性和灵活性。当您将业务逻辑划分为许多未耦合的可互换小块时,您获得了其他任何方式都难以实现的可塑性。
这种可扩展性和灵活性源于以下可能性:
自动缩放特定用例
根据接收的负载自动缩放单个服务,而不是缩放整个机器(这意味着昂贵的成本)。例如,您可以按需扩展支付服务,而保持其他用例不变。
实施独立性
用任何编程语言和任何数据库实现编写一个微服务,并对所有其他服务隐藏所有内容。
这为团队提供了很大的独立性,可以为每项工作使用正确的工具,而不受公司技术堆栈的限制。
范围崩溃
如果一项服务无法按预期工作,请控制故障范围。
您可以将每个服务视为一个输入/输出 API,并确保如果输出不是预期的,您会发现由于封装而导致的服务内部的错误。
独立可部署性
这无疑是微服务架构最重要的特性之一。您可以独立部署每个微服务,以便它们的部署变得更轻、更敏捷,并且不会破坏服务之外的任何其他功能。如果您没有实现独立可部署性,您将拥有一个 分布式单体 ,我们将在本文后面讨论。
避免开发冲突
您可以让多个团队致力于多个功能,并确保这两个开发不会发生冲突或破坏彼此的代码。
在单体应用中,更改的范围涉及整个应用程序,而不是像我们在下图中看到的那样连接到其他组件的孤立组件,因此破坏通用组件的风险更高。
Scope of Change on a Monolithic Application
Scope of change in a microservices architecture
更简单的语境化
开发人员可以更容易地理解他们的系统部分,而不是在执行简单任务之前为整个项目而苦苦挣扎。
修复组织孤岛
使用围绕业务领域建模的微服务,您可以将业务需求与其核心的 IT 架构正确对齐,而不是让产品和开发部门彼此一无所知。
“在传统的 IT 组织中,开发软件的行为通常由业务的一个完全独立的部分处理,与实际定义需求并与客户建立联系的部分完全分开。”
— Sam Newman,从单体到微服务 [4]
可能这听起来很熟悉。在许多公司中,开发和产品团队不理解为什么在这两个方向中的任何一个方向都设置了一组特定的要求。有时我们以特定的方式做事,因为 “产品部已经这样决定了” 或者因为 “开发部门说不能这样做”, 这在某种程度上是合法的。但中间需要一位了解双方的主持人,而传统的科技公司组织结构图并不能促进这一点。
IT界有句名言与此相关,叫做 康威定律 :
任何设计系统的组织都会产生其结构是组织通信结构的副本的设计。
— 梅尔文·E·康威 [5]
这就是为什么分层单体在许多小公司中如此普遍的原因,因为他们通常根据他们的技术角色(数据库管理员、后端、前端等)而不是产品职责来组建团队,并且工作得很好,但事实并非如此可扩展的。
这就是为什么微服务方法在大规模上似乎更方便,以最大限度地提高组织团队、软件架构及其与客户的关系的凝聚力和沟通,正如我们在这张图片中看到的那样。
An organizational view of the traditional IT/businnes divide (left) vs a more product-oriented approach.
为什么你不想要微服务
如您所知,它几乎在任何地方都增加了很多复杂性。第一组问题是众所周知的 分布式计算的谬误 .这些是 L Peter Deutsch 和 Sun Microsystems 的其他同事提出的一组常见断言,尽管您也可以在单体架构中找到它们,在分布式架构中这是最大化的。
总而言之,让我们提到它们中的每一个:
- 网络可靠。
- 延迟为零。
- 带宽是无限的。
- 网络是安全的。
- 拓扑不变。
- 有一名管理员。
- 运输成本为零。
- 网络是同质的。
这是一个广泛的话题,所以我将把你交给 这篇很棒的文章 如果您想了解更多信息。
除此之外,这种类型或架构也可能导致这种复杂性:
最终一致性
现在,这可能很难,这是微服务架构最严重的缺点之一。由于数据库分片,您将无法进行 酸 交易。这可能会导致数据一致性问题,您必须同步您的数据与其他服务协调。
You can’t access to a foreign database in microservices.
为此,您必须从微服务 API 访问外部数据库。有两种主要的方法来实现这一点:
- 你可以依靠一个 分布式事务(或 2PC ),由于延迟,不适合高负载。
- 或者一个 佐贺模式 ,这给交易增加了一堆复杂性。
你可以检查 这篇相关文章 的 迪尔弗鲁兹·克孜尔皮纳尔 , Garanti BBVA 的高级解决方案架构师。
分布式可观测性
监控在分布式应用程序中变得更加复杂。现在你必须从很多地方查看日志和请求指标,并且你应该尝试集中这些,否则你将失去对它的控制。
可以配置像 CloudWatch 这样的云服务来有效地做到这一点,但是有更强大的工具,如 Elastic Stack、Grafana 或 Prometheus 用于 Kubernetes 集群。
合同维护和版本控制
契约是服务和它的客户之间为了相互理解而同意的数据的形式。一个示例是必须发送到 REST API 以解析请求的 JSON 参数。
在微服务中,由于系统的解耦性质和服务的数量,它变得很棘手,因此必须对合同进行版本控制。
在知道没有其他微服务依赖于特定的版本化合约之前,每个服务都不得破坏这些版本化合约。还要记住,其他微服务可能需要回滚到需要以前的合同 [7] 的以前的代码版本。
巨石
如果我们要分析权衡,我们必须相当了解整体方法及其类型。
总结起来,至少有三种类型的单片系统。
- 单进程系统。
- 模块化单体。
- 分布式单体。
单一进程单体
是一个系统,其中所有代码都部署为 单一进程 如下图所示。出于稳健性或可扩展性的原因,您可能有此过程的多个实例,但基本上所有代码都打包到一个进程中。它们通常在代码模块之间具有薄弱或不存在的边界。这种方法的特点是高度耦合、重复代码、性能问题,并可能导致 大泥球 .
模块化单体
是单进程单体的一个子集。不同的是,每个模块都可以独立工作,但仍然需要组合部署。它修复了单进程的耦合和灵活性问题,并且可以很好地与 TDD .
对于许多组织来说,模块化单体架构可能是一个很好的选择。如果模块边界定义良好,它可以允许高度并行工作、有组织的代码和可扩展性。
甚至还有针对单个模块的分解数据库的想法,这可以很好地用于数据密集型应用程序,并很好地保持单体应用程序的简单性。
Single-process vs modular vs distributed monolith.
分布式单体
“分布式单体是一个由多个服务组成的系统,但无论出于何种原因,整个系统都必须一起部署”
— Sam Newman,从单体应用到微服务。 [6]
当架构高度耦合并且您失去可部署性独立性时,就会发生这种情况。这可能是最不方便的方法之一,因为它具有分布式系统的缺点和单进程单体的缺点。
为什么你想要一个 Monolith
主要是因为它要简单得多。在开发、基础设施、故障排除方面更简单。它们还简化了代码重用和数据一致性。
由于将它与不属于它的用例进行比较,单体应用程序受到了系统性的相当不公平的诋毁。巨石不是为大公司和成千上万的开发人员设计的。单片机是中小型项目的绝佳选择,作为一种临时设计选择,这一切都取决于权衡取舍。
一个设计良好的模块化单体可以是干净的、敏捷的和可扩展的,采用诸如 清洁架构 , 坚实的原则 , TDD , 乃至 DDD 如果没有耦合并且边界明确。这 Shopify 人们告诉我们他们对模块化单体 [8] 的实现(尽管他们在 Google Cloud 中使用数据库分片)(用他们的话来说)“ 现存最大的 Ruby on Rails 代码库之一” 它似乎对他们有用。
因此,责任永远不属于架构,而是属于架构师。
为什么你不想要一个 Monolith
随着您在这种架构中工作的更多,您会使单体变得更大,并且您的团队会成长到最终会互相妨碍的地步。您将有团队等待更改同一段代码或部署整个单体应用程序并进行自己的新更改。使用敏捷方法可以很好地工作,但再次大规模会使系统难以管理并留下性能问题。
这种类型的问题也可能发生在微服务架构中,但这种封装和设置边界的核心思想大大减少了发生冲突的机会。
结论
这一切都表明我想坚持一件事:
没有糟糕的架构类型,而是有糟糕的架构选择。
所以一旦谈到权衡取舍,就该考虑一下了。不幸的是,您的系统架构是您无法在 Google 或 StackOverflow 中搜索的内容之一,您必须找到平衡点。
我希望我能帮助让事情变得更清楚一些。在评论中写下哪些权衡不会让您入睡,如果我留下了什么或我有任何错误,也可以发表评论,感谢您的反馈。
干杯!
参考
[1] Paul G. Allen 计算机科学与工程学院:CSE490H 课程:计算设计与制造。 分布式系统简介。
[2] 理查兹 M.,福特 N.,(2020 年) 软件架构基础(一种工程方法) ,由 O'Reilly Media Inc. 编辑。章节“ 单体与分布式架构”。
[3] 纽曼 S.,(2020) 单体到微服务 ,由 O'Reilly Media Inc. 编辑。第 1 章:“足够的微服务”——用户界面
[4] 纽曼 S.,(2020) 单体到微服务 ,由 O'Reilly Media Inc. 编辑。第 1 章:“足够的微服务”——它们会产生什么问题? .
[5] 康威,梅尔文 E.(1968 年 4 月)。 “委员会是如何发明的?” . 数据化 . 14(5):28-31。 存档 从 2019-10-10 的原始版本开始。检索 2019-10-10。
[6] 纽曼 S.,(2020) 单体到微服务 ,由 O'Reilly Media Inc. 编辑。第 1 章:“足够的微服务”- 单体应用。
[7] 谷歌云文档,(2022 年) 微服务的合约、寻址和 API
[8] Shopify 的工程博客,(2019 年) 解构单体应用:设计可最大限度提高开发人员生产力的软件
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明