1. 云原生架构
1.1 概念
- 概念:
- 基于云原生技术的一组架构原则和设计模式的集合
- 旨在将云应用中的
非业务代码
部分进行最大化的剥离 - 让
云设施
接管应用中原有的大量非功能特性
- 如:弹性、韧性、安全、可观测性、灰度等
- 使业务具备轻量、敏捷、高度自动化的特点
1.2 云原生的优势
- 开发人员不需要掌握文件及其分布式处理技术、以及各种复杂的网络技术
- 存储方面:
- 将存储变成若干服务,如:对象存储服务、块存储服务、文件存储服务
- 解决了分布式场景中的各种挑战,如:高可用、自动扩缩容、安全、运维升级等挑战
- 三方软硬件:
- 把三方软硬件的能力升级成了服务,降低了开发、运维的工作量
- 非功能性特性交由平台负责
如:高可用能力、容灾能力、安全特性、可运维性、易用性、可测试性、灰度发布能力等。
- 高度自动化的软件交付
2. 云原生架构原则
2.1 服务化原则
- 服务拆分:
- 微服务架构、小服务 (MiniService) 架构
- 不同业务、不同生命周期的模块分别迭代,快整体的进度和稳定性
- 面向接口编程,高度内聚,高复用
- 基于服务流量的策略控制和治理
从架构层面抽象化业务模块之间的关系,标准化服务流量的传输
2.2 弹性原则
- 概念:
- 指系统的部署规模可以随着业务量的变化而自动伸缩
- 优点:
- 缩短了从采购到上线的时间
- 降低了企业的IT成本
- 解决了业务规模海量突发性扩张的时的资源问题
2.3 可观测原则
- 可观测性:在云平台中,主动通过日志、链路跟踪和度量等手段,使得一次点击背后的多次服务调用的耗时、返回值和参数都清晰可见,
- 优势:
- 使运维、开发、业务人员实时掌握软件运行情况
- 结合多个维度的数据指标,获得前所未有的关联分析能力
- 不断对业务健康度和用户体验进行数字化衡量和持续优化
2.4 韧性原则
-
概念:
-
软件对其依赖的软硬件异常表现出来的抵御能力,
异常包括:硬件故障、硬件资源瓶颈、业务流量超出软件设计能力、影响机房工作的故障和灾难、软件bug、 黑客攻击等因素。
-
从多个维度诠释了软件持续提供业务服务的能力
-
-
核心目标:提升软件的平均无故障时间 (Mean Time Between Failure)
-
包括:
- 服务异步化能力
- 重试/限流/降级/熔断/反压
- 重试:允许系统在一定的时间间隔内自动重新发送失败的请求,以期待其能够成功执行。
- 限流:当请求量超过系统处理能力时,拒绝或排队部分请求,以保护系统的稳定性和可用性
- 降级:为了保证核心服务的可用性,暂时牺牲一些非核心服务的功能或性能
- 熔断:当某个服务出现故障或响应时间过长时,切断对该服务的调用,以避免整个系统被拖垮
- 反压:当消费者处理速度跟不上生产者发送速度时,反压机制会通知生产者降低发送速度或暂停发送,以避免数据积压和内存溢出
- 主从模式
- 集群模式
- 可用区(Availability Zone)内的高可用
- 单元化
- 跨数据中心(Region)容灾
- 异地多活容灾:多个地理位置上同时运行业务,并对外提供服务
2.5 所有过程自动化原则
- 原因:软件技术栈的复杂度增加和组件规模的扩大,只有在所有过程自动化的前提下,才能体会到云原生技术的优势
自动化交付工具如:IaC(Infrastructure as Code)、GitOps、OAM(Open Application Model)、Kubernetes Operator
- 过程
- 标准化企业内部的软件交付过程
- 在标准化的基础上进行自动化
2.6 零信任原则
-
概念:以身份为中心进行访问控制
谁在什么环境下允许访问哪个资源
-
核心思想:
- 默认情况下不应该信任人、设备、系统(包括内网、外网)
- 需要基于认证和授权,来重构访问控制的信任基础
如IP地址、主机、地理位置、所处网络等均不能作为可信的凭证
2.7 架构持续演进原则
- 原因:而今技术和业务的快速演进,决定了几乎不可能在一开始就定义出适合软件整个生命周期的架构
3. 主要架构模式
3.1 服务化架构模式
- 概念:
- 是构建云原生应用的标准架构模式
- 要求:
- 以应用模块为颗粒度划分软件
- 以接口定义业务关系
- 以标准协议确保彼此的互联互通
- 如: HTTP、gRPC等
- 结合DDD (领域模型驱动)、 TDD (测试驱动开发)、容器化部署提升每个接口的代码质量和迭代速度
- 典型模式:
- 微服务
- 小服务模式:
- 概念:一组关系非常密切的服务的组合,它们共享数据
- 适用:超大型的软件系统
- 作用:避免接口的颗粒度太细,而导致过多的调用损耗和治理复杂度
- 优势
- 在进程级实现了模块的分离,每个模块(接口)作为单独的服务,都可以单独升级,从而提升了整体的迭代效率
3.2 Mesh化架构模式
- 概念
- 把中间件框架从业务进程中分离
- 业务进程中只保留很“薄”的 Client部分
- Client通常很少变化,只负责与Mesh 进程通信
- 原来需要在SDK中处理的流量控制、安全等逻辑由Mesh进程完成
3.3 Serverless模式
- 概念
- 一种无服务器运算模型
- 以平台即服务(PaaS)为基础
- 客户不需要部署、配置、管理服务器服务,代码运行所需要的服务器服务皆由云端平台来提供
- 适用范围:
- 事件驱动的数据计算任务
- 计算时间短的请求/响应应用
- 没有复杂相互调用的长周期任务
不适用:
- 有状态的服务
- 长时间后台运行的密集型计算任务
- 频繁的外部I/O 的服务
3.4 存储计算分离模式
- 概念:
- 把各类暂态数据(如session)、 结构化和非结构化持久数据,都采用云服务来保存,从而实现存储计算分离
问题:一些状态如果保存到远端缓存,会造成交易性能的明显下降
3.5 分布式事务模式
- 提倡每个服务使用私有的数据源
3.5.1 XA模式
- 概念:一个事务通常被分为两个阶段:准备阶段和提交阶段。
- 优点:一致性好
- 缺点:性能差
3.5.2 基于消息的最终一致性 (BASE)
- 优点:性能高
- 缺陷:通用性有限
3.5.3 TCC模式
- 概念
- Try-Confirm-Cancel
- 是分布式事务中的一种二阶段提交协议
- 将业务处理过程拆分为三个阶段:
- Try(尝试):主要对业务资源进行检查并预留
- Confirm(确认):在Try阶段成功后,对业务处理进行提交
- Cancel(取消):如果Try或Confirm阶段失败,则进入Cancel阶段,执行回滚操作,释放Try阶段预留的资源。
- 优点:
- 完全由应用层来控制事务,事务隔离性可控
- 较为高效
- 缺点:
- 对业务的侵入性强
- 设计开发维护等成本很高
3.5.4 SAGA模式
- 概念:
- 每个正向事务都对应一个补偿事务
- 优点:(教材原文是:优缺点和TCC模式类似)
- 缺点:开发维护成本高
3.4.5 SEATA的AT模式
- 优点:
- 高性能
- 无代码开发工作量
- 可以自动执行回滚操作
- 缺点:存在使用场景限制
3.6 可观测架构
-
可观测架构包括以下三个方面
-
Logging:提供多个级别的详细信息跟踪,由应用开发者主动提供
级别如:verbose、debug、warning、error、fatal
-
Tracing:提供一个请求的完整调用链路跟踪
对于分布式场景尤其有用
-
Metrics:提供对系统量化的多维度度量
-
-
可观测的开源框架:
- Open Tracing:是一个分布式跟踪系统
- Open Telemetry:是一个用于解决可观测性大一统的框架
-
操作
- 选择框架
- 规范上下文的可观测数据
- 规划可观测数据的传播
- 确保进行分布式链路分析时有足够的信息进行快速关联分析
3.7 事件驱动架构(EDA)
- Event-Driven Architecture
事件驱动架构不仅用于(微)服务解耦,还可应用于下面的场景中:
1)增强服务韧性
由于服务间是异步集成的,下游的任何处理失败都不会被上游感知,即,不会对上游带来影响。
2)命令查询职责分离(CQRS)
- 概念:
- Command Query Responsibility Segregation
- 把对服务状态有影响的命令用事件来发起
- 对服务状态没有影响的查询才使用同步调用的API接口
- 数据一致性的保障:当需要重新构建服务状态时,把EDA (事件驱动架构)中的事件重新“播放”一遍即可。
3)数据变化通知
在服务架构下,往往一个服务中的数据发生变化,另外的服务会感兴趣,比如用户订单完成后,积分服务、信用服务等都需要得到事件通知并更新用户积分和信用等级。
4)构建开放式接口
事件的提供者并不用关心有哪些订阅者
5)事件流处理
- 场景:大量事件流(而非离散事件)的数据分析场景
- 如:基于Kafka 的日志处理
6)基于事件触发的响应
在 IoT时代大量传感器产生的数据,不会像人机交互一样需要等待处理结果的返回,天然适合用EDA来构建数据处理应用。
4. 典型的云原生架构反模式
4.1 庞大的单体应用
- 问题:于缺乏依赖隔离
- 包括:
- 代码耦合带来的责任不清
- 模块间接口缺乏治理而带来变更影响扩散
- 不同模块间的开发进度和发布时间难以协调
- 一个子模块不稳定导致整个应用都变慢
- 扩容时只能整体扩容,而不能对达到瓶颈的模块单独扩容
- 解决:
- 通过服务化进行一定的拆分
- 通过业务关系确定主要的服务模块以及这些模块的边界
- 清晰定义模块之间的接口
- 让组织关系和架构关系匹配(吐槽:又不一定是企业信息化,总带一句组织关系,不合适吧)
- 通过服务化进行一定的拆分
4.2 单体应用“硬拆”为微服务
拆分不当的例子如下:
- 小规模软件的服务拆分
软件规模不大,团队人数也少,但是为了微服务化,强行把耦合度高、代码量少的模块进行服务化拆分,一次性的发布需要拆分为多个模块分开发布和维护。
- 数据依赖
服务虽然拆分为多个,但由于它们的数据是紧密耦合的,仍然需要共享数据库,造成服务间数据依赖。
- 性能降低
拆分后,原来的本地调用变成了分布式调用,导致整个服务链路性能急剧下降。
4.3 缺乏自动化能力的微服务
-
后果
-
软件开发周期变长
接口增加 ——> 用例增加 ——> 排队测试 ——> 软件发布周期变长
-
运维成本增加
更多的进程运行于同一环境,运维难度增加
-
风险提升
更多的进程运行于同一环境,缺乏自动维护,可能造成不可重现的影响
-