软件工程无银弹。
模式名称
微服务本质上是一种架构风格。和单体(巨石架构)架构类似。
微服务架构模式。
需求
微服务是指将大型复杂软件应用拆分成多个简单应用,每个简单应用描述着一个小业务,系统中的各个简单应 用可被独立部署。 各个微服务之间是松耦合的,可以独立地对每个服务进行升级、部署、扩展和重新启动等流程, 从而实现频繁更新而不会对最终用户产生任何影响。相比传统的单体架构,微服务架构具有降低系统复杂度、独立部署、独立扩展、跨语言编程等特点。
与此同时,架构的灵活、开发的敏捷同时带来了运维的挑战。微服务框架作为微服务开发和运行治理的必要支 撑,帮助实现微服务注册、发现、治理等能力,目前,在微服务技术架构实践中主要有侵入式架构和非侵入式架构 两种实现形式。侵入式架构是指服务框架嵌入程序代码,实现类的继承,其中以 Spring Cloud 最为常见。非侵入式 架构,如 Istio ,则是以代理的形式,与应用程序部署在一起,接管应用程序的网络且对其透明。
模式描述
当前架构的设计模式描述。 使用模式语言进行描述。
微服务架构的示意图
微服务架构下,应用程序由许多独立的可替换的小型服务构成,每个服务实现单个业务功能,服务间松散耦合,通过 API 网关完成调用。
- 什么是服务?服务是单一的,可以独立部署的软件组件,它实现的特定的功能。
- 服务大小的粒度切割很关键,它是架构中的「架构量子」。
- 微服务的本质:服务的分解和定义。
- 单体系统 v.s. 微服务化的架构设计差异
- 单体地狱的理解
- 软件组织分工问题
- 单体随着业务复杂度的增加,会过度复杂,比如 SOA 架构,开发的速度也会成为问题
- 难以扩展,随着体系结构的放大
- 微服务的架构设计往往意味着每个服务有独立的数据库
- 单体地狱的理解
实现关注
微服务的拆分思路 & 策略
- 适度选择微服务的架构风格:分层、六边形风格
- 一般的拆分过程:功能需求 => 领域模型 => 定义系统操作 & 业务能力 => 拆分服务
- 充分使用领域驱动设计中提供的服务拆分概念,连接:领域模型(DomainModel)、界限上下文(BoundingContext)等设计约束去做服务拆分
- 充分利用面向对象设计原则,比如 OCP、SRP 原则
微服务进程之间的通信策略
- 单机通信:关注主流的 IPC 机制
- 服务之间同步通信:使用主流的 RPC 机制
- 关注 RPC 的性能:序列化、传输性能 …
- 关注 RPC 传输数据格式:JSON、Protobuf、XML …
- 关注 API 的设计风格:RESTful、GraphQL 等
- 服务之间使用异步事件
- 消息中间件的选型策略
- 关注:支持编程语言、消息标准、消息排序、持久性、耐久性、可扩展性、延迟、竞争性 …
- 关注:并发消息的处理策略、重复消息的幂等性、事务性质的消息处理
微服务之间的事务处理
- Paxos 作为分布式算法的基石
- 充分考虑 AICD 刚性事务(本地事务)的设计风格
- 多服务保持一致性的分布式事务:SAGA 事务
微服务的业务逻辑设计
- 使用 DDD 指导设计,建立团队之间的设计原则
- 只引用聚合根
- 聚合之间使用主键进行引用
- 单个事务只能够进行单个聚合更新
- 控制聚合的粒度,做合理切分
- 使用领域事件
- …
- 使用事务脚本
- 使用事件溯源的模式开发业务逻辑
- 微服务架构之中的查询设计两者选型的利弊需要权衡。
- API 组合模式
- 客户端组合
- 服务端组合
- BFF
- GraphQL
- 命令查询职责隔离模式(CQRS)
- 使用领域事件维护另一个数据视图变更(数据库)
- 让查询库(Q)和更新库(CUD)分离职责
- API 组合模式
- 外部 API 模式
- API Gateway 模式
- Ingress 就是一套网关架构
- BFF 网关也是类似的架构模式
- 模式:Schema 机制驱动查询
- API Gateway 也可以承载服务发现的能力
- API Gateway 模式
微服务的持续集成 & 测试
- 单元测试
- 集成测试
- 组件 & 服务测试
- E2E(端到端测试)
必须在 DevOPS 流程中得到淋漓尽致的展现。
面向生产的微服务架构关注点
- 关注 流量治理 的机制
- 服务容错:常见的容错策略(摘录自凤凰架构)
- 故障转移 Failover:一个副本失败之后,尝试转移请求到别的副本
- 快速失败 Failfast:对于幂等的服务,可以使用该策略,直接舍弃请求,抛出异常
- 安全失败 FailSafe:主路和旁路的失败,分开治理
- 沉默失败 FailSilent:请求失败之后,将节点挂置一段时间之后再恢复服务
- 故障恢复 Failback:故障后重启失败的节点,恢复服务
- 并行调用 Forking:触发多个节点,双重保险
- 广播调用 Brodcast:触发多个节点,谁快谁上
- 流量控制
- 理解流量统计的指标:每秒事务数(TPS)、每秒请求数(HPS)、每秒查询数(QPS)
- 限流的设计模式:流量计数、滑动时间(Sliding Window Algorithm)、漏桶模式(Traffic Shaping)、令牌漏桶模式
- 分布式限流:存入集中式缓存(Redis)等在集群内实现流量分发和共享
- 服务容错:常见的容错策略(摘录自凤凰架构)
- 关注 可靠通信 的机制
- 安全机制
- 0 信任网络
- …
- 关注 可观测性 的机制
- 日志系统
- 追踪系统
- …
- 使用 服务网格 来隔离微服务治理与业务逻辑
- 边车代理模式 SideCarProxy
- 关注数据平面、控制平面
- 关注主要的 ServiceMesh 相关的生态
部署微服务策略
- 应用打包和制件机制
- 虚拟化技术 & 虚拟化容器
- 指令集虚拟化(ISA):指令集虚拟化
- 硬件抽象层虚拟化(HAL):VMVare 等桌面端虚拟机
- 操作系统层虚拟化(OSL):Docker
- 容器虚拟化(本质上是不同维度的封装)
- 隔离文件技术:chroot
- 隔离访问技术:Linux Namespace
- 隔离资源技术:cgroups
- 封装系统:LXC
- 封装应用:Docker
- 封装集群:Kubernate
- 以容器构建系统
- 隔离 & 协作:K8S 提出了 Container ⇒ Pod(容器组) ⇒ Node(集群中的单台物理机) ⇒ Cluster(集群) ⇒ Federation(多个集群组建的可以用于区域多活、跨地域容灾等诉求) 的不同粒度的结构分离
- 韧性 & 弹性:自动扩容、回滚更新、故障恢复等特征关注。
- 以应用为中心的封装:
- Kustomize:用配置文件来配置配置文件
- Helm & Chart:封装 K8S 应用涉及的所有资源,后提以类型 Linux 的包管理机制做安装
- Operator & CRD:封装、部署和管理 K8S 应用的方法。
- 容器编排体系
- 使用 Serverless 相关的技术,0 关注运维模式
- FaaS
实现结果
当前架构期望达到的目的 & 实现的效果。
微服务架构中的小型服务具有以下特点:
- 单个服务规模小、相互独立且松散耦合,可以由小团队来完成开发和维护,并通过自动化流程部署和发布。
- 每个服务的代码库都是独立的,可以单独进行集中式管理。服务支持独立的自动化部署,更新时无需重新部署整个应用程序。
- 每个服务负责维护自己的数据和状态,不同于N层架构,没有单独的数据层负责数据持久性。单个服务很容易被替换。
- 服务之间通过HTTP、REST API 或其他标准的轻量级通信协议进行通信,每个服务内部的实现细节均对其他服务隐藏。服务之间不需要使用相同的技术栈、库或框架。
微服务架构实现之后重要的特征:
- 围绕业务能力构建 Organized around business capability
- 分散治理 Decentralized Governance
- 通过服务来实现独立的组件 Componentization via Service
- 产品化思维 Product not project
- 数据去中心化 Decentralized Data Management
- 强终端弱管道 Smart EndPoint and DumbPipe
- 容错性设计 Design for fault
- 渐进式设计 Evolutionary Design
- 基础设施自动化 Infrastructure Atuomation
相关架构模式
- 单体架构
- 事件驱动的架构
- SOA 风格
- 微内核架构
评价
微服务架构的优点
- 敏捷:支持细粒度的独立迭代和发布,速度快。在很多传统应用程序中,如果在应用程序的一个组件中发现缺陷,整改发布流程都会被阻塞,需要等缺陷修复后才能进行集成、测试和发布,造成新功能发布延期。由于微服务架构中的每个小型服务是独立部署的,您可以轻松地对单个服务进行缺陷修复或者特性变更,无需重新部署整个应用程序,一旦发现缺陷立刻回滚服务。
- 支持更小型、更专注的团队。单个小型服务仅需要一个小的开发团队就可以完成开发、测试和部署工作。相比之下,更大的团队通常意味着更低的沟通效率、更高的管理开销和更低的工作效率。
- 支持更小型的代码库。在应用程序中,代码依赖项往往会随着时间的推移而变得繁杂。当您需要更新功能时,对应的可能要更新多处代码。由于微服务的代码库和数据存储都是独立的,所以可以最大程度地简化代码依赖,降低更新工作量。
- 允许多种技术混搭,技术栈不受限制。您可以为每种服务选择最合适的技术,支持混合技术栈。
- 故障隔离。在系统中出现问题时,例如内存泄露、数据库连接未关闭等情况,将仅仅只会影响单个微服务,不一定导致整个应用程序的中断。
- 数据隔离。对于传统架构的应用程序,数据可能会被不同的服务调用,数据模式的升级风险很高。对于微服务架构,仅有单个服务受到影响,数据模式的升级风险较低。
- 可伸缩性、扩展性和可维护性高。每个服务都支持独立水平扩展,无需扩展整个应用程序,资源的利用率高,扩展快速。每个微服务都可以独立地进行服务升级更新,结合持续集成工具可以进行持续发布,快速完成服务升级发布流程。
微服务架构的挑战
微服务架构虽然解决了很多问题,但使用起来也有自己的挑战:
- 复杂性。与传统架构的应用程序相比,微服务架构的组件更多:每个独立的小型服务更简单,但是整个系统整体来讲更复杂了。服务数量多,也意味着部署和管理的工作量更大。同时,您可能还需要考虑分布式系统的复杂性和分布式事务处理难度。
- 开发、测试、部署难度。开发依赖于其他独立服务的小型服务时,需要的方法与编写传统应用程序不同,现有工具并非总是能够处理好服务间的依赖关系。此外,服务拆分后,几乎所有功能都会涉及多个服务。原本单个程序的测试会变为服务间调用的测试、模块与模块之间的联调测试,测试会变得更复杂 。
- 运维难度。由于在开发过程中可能采用了许多不同的语言和框架,应用程序可能会变得难以维护。此外,服务数量变多,会导致有服务出现故障的概率增大,而整个应用分散成多个服务,也会导致问题定位更加困难。这种情况下,建议您充分使用各种 链路追踪 和日志分析组件,提升可维护性。
- 网络拥塞和延迟。使用大量小型服务可能会增加服务间的通信量。此外,如果服务间的依赖关系链过长,可能会进一步增加延迟。
- 保证数据完整性和一致性的难度。每个小型服务都仅负责自己的数据持久性,因此,不同服务间的数据一致性很难保证。
- 版本控制难度。 由于服务相对独立,更新某个服务时不必中断依赖于它的服务,更新时间随意且频度高,可能会导致服务间的兼容性问题。
- 考验组织的成熟度,并非所有组织形态和合作模式都适合微服务架构。
微前端架构的借鉴
关注视角 | 微服务架构 | 微前端架构 |
---|---|---|
康威定律:组织和软件架构 研发团队松散耦合(耦合度) 小而自治、跨功能型团队 |
- 服务拆解 - 独立的研发流程 - 独立的技术栈 |
- 应用拆解(代码拆解,杜绝寡头应用) - 独立的研发流程 - 独立的技术栈 |
服务拆分 | - 粒度控制(架构量子) - DDD 方法论指导设计 |
- 微前端应用的结构拆解 - 按照业务应用 or 页面拆解 |
服务之间进程通信 | - RPC 系统 - gRPC - JSONWeb - 通信格式 - JSON、XML、Protobuf - RESTful 设计 |
- window.postMessage - Virtual API 机制 - 直接 API 访问 - Shared Namespace - … |
隔离机制 | - Pod(虚拟机制) - Docker 容器 |
- JS 沙箱系统设计 - CSS 沙箱系统设计(ShadowDOM) - ThreadWorker 机制设计 - iFrame 隔离机制(0 信任机制) |
单元可以独立扩展 | - SPI - 服务整体替换(服务路由) |
- 控制反转和依赖注入系统做扩展 - 子模块模式(可替换模式) |
单元可独立部署、研发和交付 | - DevOps 过程 |
- DevOPS 过程 |
单元之间具备更好的容错性 | - 服务治理中关于「服务容错」 |
- 灰度方案 - 降级方案:天然支持 故障隔离 - A / B Test |
独立可测试性 | - 单元 / 集成 / 组件 / E2E 测试 |
- 单元测试 / 集成测试 / E2E 测试 |
独立可观测性 | - 日志 - 分布式追踪 |
- 宿主和子应用的监控机制 - 故障链路 Trace 机制 |
服务治理(网格治理) | - 健康检查 - 日志聚合 - 分布式追踪 - 异常追踪 - 审计 - 流量控制 |
- 前端应用的体验、性能大盘(宿主、微应用) - 灰度状态 & A|B Test 效果 |
分布式一致性保证 | - ACID - 事务一致性 |
不考虑 |
部署模式 | - 容器化 - 容器编排 K8S - Serverless |
- CDN 模式 - 随服务端部署(专有云 or 混合云模式) |
服务安全 | - 认证协议 - 0 信任网络 |
- 验权机制 - iFrame 机制 |