就应用设计最佳实践和原则而言,构建复杂的基于容器的架构与编程没有太大区别。本文的目标是使用众所周知的编程原理从开发人员的角度展示三种流行的可扩展性架构模式。
让我们从单一职责原则开始。根据 R. Martin 的说法,"一个类应该只有一个改变的理由。" 但类是用于简化现实世界问题并表示软件组件的抽象。因此,一个组件应该只有一个随时间变化的原因。软件服务,特别是微服务也是组件(运行时组件),应该只有一个改变的理由。微服务应该是单个可部署单元,这意味着它们独立于其他组件进行部署,并且可以根据需要拥有任意数量的实例。
但这总是正确的吗?微服务是否始终作为单个单元部署?
在Kubernetes中,微服务的体现是 Pod。Pod 被定义为一组共享文件系统、内核命名空间和 IP 地址等资源的容器。Pod 是 Kubernetes 集群中调度的原子单元,每个 Pod 旨在运行给定应用程序的单个实例。
根据文档,"Pod 旨在支持形成一个内聚服务单元的多个协作进程(作为容器)。Pod 中的容器会自动在集群中的同一物理或虚拟机上共同定位和共同调度"水平扩展应用程序意味着复制 Pod。根据 Kubernetes 文档,可以使用两种策略来配置 Pod:
运行单个容器的 Pod:"每个 Pod 一个容器"模型是最常见的 Kubernetes 用例;Pod 是单个容器的包装器,Kubernetes 管理 Pod,而不是直接管理容器。
运行多个容器一起工作的 Pod:Pod 可以封装由多个紧密耦合且需要共享资源的共置容器组成的应用程序。这些位于同一位置的容器形成了一个单一的内聚服务单元,例如,一个容器向公众提供存储在共享卷中的数据,而一个单独的sidecar容器则刷新或更新这些文件。Pod 将这些容器、存储资源和临时网络身份包装在一起作为一个单元。"
答案是:不!微服务并不总是作为单个单元部署!
除了可扩展性模式、部署和可靠性模式等一些流行的云架构模式之外,还有可扩展性架构模式。我们将仔细研究云架构的三种最流行的可扩展性模式:
- 边车模式
- 大使模式
- 适配器模式
边车模式
问题
每个可部署的服务/应用程序都有自己的"更改原因"或责任。然而,除了其核心功能之外,它还需要做其他事情,在软件开发人员术语中称为"横切关注点"。一个示例是需要发送到监控服务的性能指标的集合。另一种方法是记录事件并将其发送到分布式日志服务。我将它们称为横切关注点,因为它们不直接与业务逻辑相关并且是多个服务所需要的,它们基本上代表了需要成为每个部署单元一部分的可重用功能。
解决方案
这个问题的解决方案被称为边车模式,并且要求创建一个称为边车容器的额外容器。边车容器是主容器的扩展,遵循开放-封闭设计原则(开放于扩展,封闭于修改)。它们在部署方面与"主"容器紧密耦合,因为它们作为同一Pod的一部分部署,但仍然容易替换,并且不破坏扩展容器的单一责任。此外,实现的模块化允许对与业务相关的功能和其他助手服务(如事件记录或监控)进行独立测试。两个容器之间的通信是快速和可靠的,它们共享对相同资源的访问,使助手组件能够提供可重用的基础架构相关服务。此外,它适用于许多类型的服务,解决了在使用不同技术方面的异质性问题。边车组件的升级也很简单,因为它通常意味着升级Docker容器版本,并使用例如Kubernetes的无停机时间策略进行重新部署。
大使容器
问题
已部署的服务并不是孤立运行的。它们通常通过网络与其他服务进行通信,甚至在单个组织控制的应用程序或软件平台之外。组件之间的集成通常意味着与外部 API 的集成以及处理外部系统的故障或不可用。外部系统集成的常见做法是定义所谓的 API Facade,即隐藏外部系统 API 复杂性的内部 API。API Facade 的作用是隔离外部依赖项,提供内部 API 定义的实现,并在需要时负责安全性和路由。此外,外部系统的故障和不可用通常使用一些常见模式(例如重试模式、断路器模式)进行处理,有时还由本地缓存提供支持。所有这些技术细节都会使主要服务变得复杂,并且似乎是辅助容器的候选者。
解决方案
该问题的解决方案被称为大使模式,并意味着创建一个称为大使容器的额外容器。大使容器将本地连接代理到外部世界,它们基本上是一种边车容器。这种容器组合很强大,不仅因为关注点的分离和不同团队可以轻松地拥有组件,而且它还允许轻松地模拟本地开发环境的外部服务。
适配器容器
问题
仍有许多整体系统计划迁移到更轻量级的架构。然而,迁移不可能一次性完成,而且在两个版本的系统中都支持添加新功能的同时,等待整个系统重写多年也是有风险的。迁移应该以小块的形式进行,发布单独的服务并将它们一一集成。这个过程会重复进行,直到遗留的整体系统消失。因此,我们有一个支持新 API 的系统新部分和一个仍然支持旧 API 的旧系统部分。例如,我们可能新实现了 REST 服务,但仍然有一些旧的基于 SOAP 的服务。我们需要一些东西来负责暴露旧功能,就好像所有服务都已迁移并且可以由客户系统集成一样。
解决方案
该问题的解决方案称为适配器或反腐败模式。适配器容器负责从一种通信协议转换为另一种通信协议以及从一种数据模型转换为另一种数据模型,同时向外部世界隐藏实际服务。此外,适配器容器可以提供双向通信。如果遗留系统需要与新服务通信,它也可以是该通信的适应组件,充当一种大使容器,直到迁移完成。
在本文中,我们了解了容器组合如何在不实际更改主应用程序容器的情况下提供可扩展性机制,通过允许将复合 Pod 视为在微服务架构中公开单个简单服务的任何其他简单 Pod 来提供稳定性和可重用性。有人会问为什么不使用一个库并在多个容器之间共享它。好吧,这也是一个解决方案,但我们面临着在使用它的所有服务之间引入耦合的共同责任问题。此外,异构服务需要使用所有支持的语言重写库。这也打破了单一责任原则,无论如何我们都希望保留这一原则。
作者:Daniela Kolarova
更多内容请关注公号【云原生数据库】
squids.cn,云数据库RDS,迁移工具DBMotion,云备份DBTwin等数据库生态工具。