主要体现在早期
架构的重要性影响应用的非功能性需求,也称为质量属性或者其他的能力。最显著的影响软件交付速度的可维护性、可扩展性和可测试性。
微服务架构定义为面向服务的架构,它们由松耦合和具有边界上下文的元素组成。
扩展一个应用程序的三种维度:X,Y和Z
x轴扩展:在多个实例之间实现请求的负载均衡
z轴扩展:根据请求的属性路由请求。不同于x轴扩展,每个实例仅负责数据的一个子集。相当于根据数据作分区。
y轴扩展:根据功能把应用拆分为服务,x轴和z轴扩展有效地提升应用的吞吐量和可用性,没有解决日益增长的开 问题和应用复杂性。y轴扩展也就是功能性分解。
使用服务作为模块化的单元。
每一个服务之间都是松耦合的,服务之间仅通过API进行通信。实现松耦合的方式这一是每个服务都拥有自己的私有数据库。
SOA | 微服务 | |
服务间通信 | 智能管道,如Enterprise Service Bus(ESB),往往采用重量级协议如SOA或其他WS*标准 | 使用哑管道,如消息代理或者服务之间点对点通信,使用REST或者gRPC类的轻量级协议 |
数据管理 | 全局数据模型并共享数据库 | 每个服务都有自己的数据模型和数据库 |
典型服务的规模 | 较大的单体应用 | 输小的服务 |
模式分为三组
服务拆分的相关模式
通信的相关模式
实现事务管理的数据一致性相关模式
查询数据的相关模式
服务部署相关模式
可观测性相关模式
自动化测试相关模式
基础设施和逻辑问题的相关模式
安全相关模式
计算机系统的软件架构是构建这个系统所需要的一组结构,包括软件元素、它们之间的关系以及两者的属性
应用程序的架构是将软件分解为元素和这些元素之间的关系。软件架构的4+1视图模型
应用程序有两个层面的需求。第一类是功能性需求,这些需求决定一个应用程序做什么,通常包含在用例或者用户故事中。应用的架构其实跟这些功能性需求没什么关系。架构的重要性在于,帮助应用程序满足第二类需求,非功能性需要,也称为质量属性需求,或者简称为能力。这些非功能性需求决定一个应用程序在运行时的质量,比如可扩展性和可靠性,它们也决定了开发阶段的质量,包括可维护性,可测试性,可扩展笥和可部署性。
流行的三层架构是应用于逻辑视图的分层架构
分层架构弊端:
是分层架构风格的替代品。六边形架构风格选择以业务逻辑为中心的方式组织逻辑视图。应用程序具有一个或多个入站适配器,而不是表示层,它通过调用业务逻辑来处理来自外部的请求。同样具有一个或者多个出站适配器,而不是数据持久化层,这些出站适配器由业务逻辑调用外部应用程序 。此架构的一个关键特性和优点是业务逻辑不依赖于适配器,相反,各种适配器都依赖于业务逻辑。
服务:是一个单一,可独立部署的软件组件,实现了一些有用的功能。
松耦合:服务之间的松耦合
共享类库的角色:一些通用的功能打包到库或者模块中。
步骤
系统操作:应用程序必须处理的请求的一种抽象描述,既可以是更新数据的命令,也可以检索数据的查询。每个命令的行为都是根据抽象领域模型定义的。
分解服务:有几种策略,一种是源于业务架构学派的策略是定义与业务能力相对应的服务,另一种策略是围绕领域驱动设计的子域来分解和设计服务。
确定服务API和协作:将第一步中标识的每个系统操作分配给服务,可以完全的独立地实现操作,可能需要与其他服务协作,在这种情况下,可以确定服务的协作方式,通常需要服务来支持其他操作。
子域是领域的一部分。领域模型的边界为限界上下文。
服务可以使用基于同步请求/响应的通信机制,如REST和gRPC。也可以使用异步的基于消息的通信机制。消息模式也不尽相同,可以基于JSON或XML,也可以基于二进制的 Avro或Protocol Buffers格式 。
有两个维度,一个关注的是一对一和一对多。
一对一:每个客户端请求由一个服务实例来处理
一对多:每个客户端请求由多个服务实例来处理。
第二个维度关注的是同步和异步。
同步模式:客户端请求需要服务端实时响应,客户端等待响应时可能导致堵塞
异步模式:客户端请求不会阻塞进程 ,服务端的响应可以是非实时的。
设计良好的接口暴露有用功能隐藏实现细节。
使用接口定义语言(IDL)
API优先设计
语义化版本控制,是一组规则,用于规定如何使用版本号,并且以正确的方式递增版本号。
要求版本号由三部分组成:MAJOR.MINOR.PATCH。MAJOR当你对API进行不兼容的更改时。MINOR当你对API进行向后兼容的增强时。PATCH当你进行向后兼容的错误修复时。
分为文本和二进制。
REST定义了成熟度模型,有四个层次
Level0:只是向服务端点发起HTTP POST请求,进行服务调用,每个请求都指明了需要执行的操作、操作针对的目标和必要的参数。
Level1:引入了资源的概念,要执行对资源的操作,客户端需要发出指定要执行的操作和包含任何参数的POST请求。
Level2:使用HTTP动词来执行操作。请求查询参数和主体指定操作的参数。
Level3:基于HATEOAS原则设计,基本思想是在由GET请求返回的资源信息中包含链接。
REST好处:
弊端:
是基于二进制消息的协议,可以使用基于Protocol Buffer的IDL定义gRPC API。可以使用Protocol Buffer编译器生成客户端的桩和服务端骨架。
gRPC API由一个或多个服务和请求/响应消息定义组成。服务定义类似于Java接口,是强类型方法的集合,除了支持简单的请求/响应RPC之外,gRPC还支持流式RPC。服务器可以使用消息流回复客户端。客户端也可以向服务发送消息流。
弊端:
针对第一条,使用以下机制
网络超时:在等待针对请求的响应时,一定不要做成无限阻塞,而是要设定一个超时,使用超时可以保证不会一直在无响应的请求上浪费资源。
限制客户端向服务端发出请求的数量:把客户端能够向特定服务发起的请求设置一个上限,如果请求达到了这样的上限,很有可能发起更多的请求也无济于事,应该让请求立刻失败。
断路器模式:监控客户端发出请求的成功和失败数量,如果失败的比例超过一定的阈值,就启动断路器,让后续的调用立刻失效。如果大量的请求都以失败而告终,说明被调服务不可用。在经过一段时间后,客户端应该继续尝试,如果调用成功,则解除断路器。
对于第二条,使用以下机制
根据具体情况决定如何从无响应的远程服务中恢复你的服务,一种选择是服务只是向其客户端返回错误。另外一种是返回备用值可能会有意义。
两种实现方式
应用层服务发现模式
这种服务发现模式是两种模式的组合。第一种模式是自注册模式。服务实例调用服务注册表的注册API来注册其网络位置。第二种模式是客户端发现模式。当客户端想要调用服务时,会查询服务注册表以获取服务实例的列表。
好处:
可以处理多平台部署的问题
弊端:
需要 为使用的每种编程语言提供服务发现库。
平台层服务发现模式
以下两种模式组合
使用消息代理 或者无代理架构。
消息由消息头部和消息主体组成。标题是名称与值对的集合,描述正在发送的数据的元数据。消息头部包含消息发送者提供的名称与值对之外,还包含其他信息,如发件人或消息传递基础设施生成的唯一消息ID,以及可选的返回地址,该地址指定发送回复的消息通道。消息正文是以文本或者二进制格式发送的数据。
消息类型包含
消息通道类型
使用消息代理实现消息通道。ActiveMQ用队列和主题 ,RabbitMQ用交换和队列
常见的解决方案是使用分片。
理想情况下,消息代理应该只传递一次消息,但保证有且仅有一次的消息传递通常成本很高,大多数消息代理承诺至少成功传递一次消息。
处理重复消息方式:
使用数据库表作为消息队列
发送消息的服务有一个OUTBOX数据库表,作为创建、更新和删除业务对象的数据库事务的一部分,服务通过消息插入到OUTBOX表中来发送消息。这样可以保证原子性。OUTBOX表充当临时消息队列,MessageRelay是一个读取OUTBOX表并将消息发布到消息代理的组件。
通过轮询模式发布事件
让MessageRelay在表中轮询未发布的消息。定期查询表,把这些消息发送给消息代理,它把每个消息发送给它们的目的消息通道。最后,MessageRelay把完成发送的消息从OUTBOX表中删除。
事务日志拖尾模式发布事件
让MessageRelay拖尾数据库的事务日志文件。每次应用程序提交到数据库的更新都对应着数据库事务日志中的一个条目。事务日志挖掘器可以读取事务日志,把每条跟消息有关的记录发送给消息代理。
异步交互模式,采用复制数据的方式来提高可用性。服务维护一个数据副本,这些数据是服务在处理请求时需要使用的,这些数据的源头会在数据变化时发生消息,服务订阅这些消息来确保数据副本的实时更新。
saga只满足ACD特性,缺乏隔离性。
saga协调有两种方式:
每个服务都有自己的私有数据库,需要一种机制来保障多数据库环境下的数据一致性。
在多个服务、数据库和消息代理之间维持数据一致性的传统方式是采用分布式事务。分布式事务管理的事实标准是XA。XA采用了两阶段提交来保证事务中的所有参与方同时完成提交,或者在失败时同时回滚。
应用程序的整个技术栈需要满足 XA标准,包括符合XA要求的数据库、消息代理、数据库驱动、消息API,以及用来传播XA全局事务ID的进程间通信机制。问题是许多新技术,包括NoSQL数据库,并不支持XA标准的分布式事务。同样,一些流行的消息代理,如RabbitMQ和Apache Kafka并不支持分布式事务。
分布式事务的另一个问题在于,本质上都是同步进程间通信,这会降低分布式系统的可用性。为了让一个分布式事务完成提交,所有参与事务的服务都必须可用。
Saga松耦合,异步服务的。
Saga是一种在微服务架构中维护数据一致性的机制,它可以避免分布式事务所带来的问题。一个Saga表示需要更新多个服务中数据的一个系统操作。Saga由一连串的本地事务组成。每一个本地事务负责更新它所在服务的私有数据库。
使用补偿事务回滚Saga。使用异步消息进行通信。当本地事务完成后,服务会发布消息,使用消息可以确保Saga参与方之间的松散耦合,还可以保证Saga完成。
好处
弊端
编排器作为一个状态机,由一组状态和一组由事件触发的状态之间的转换组成。每个转换都可以有一个动作。动作就是对某个参与方的调用。状态之间的转换由Saga参与方执行的本地事务完成触发。当前状态和本地事务的特定结果决定状态转换以及执行的动作。使用状态机模型可以更轻松地设计、实现和测试Saga。
好处
弊端
在编排器中存在集中过多业务逻辑的风险。
语义锁:应用程序级的锁
交换式更新:把更新操作设计成可以按任何顺序执行
悲观视图:重新排序Saga的步骤,以最大限度地降低业务风险
重读值:通过重写数据来防止脏写,以在覆盖数据之前验证它是否保持不变
版本文件:将更新记录下来,以便可以对它们重新排序
业务风险评级:使用每个请求的业务风险来动态选择并发机制。
一个Saga包含三种类型的事务
可补偿性事务:可以使用补偿事务回滚的事务
关键性事务:Saga执行过程的关键点。如果关键性事务成功,则Saga将一直运行到完成。关键性事务不一定是可补偿性事务,或者可重复性事务。但是它可以是最后一个可补偿的事务或第一个可重复的事务。
可重复性事务:在关键事务之后的事务,保证成功。
分为面向过程的事务脚本模式和面向对象的领域建模模式
重要特征是实现行为的类与存储状态的类是分开的。
脚本位于服务类中,每个服务类都有一个用于请求或者系统操作的方法。这个方法实现请求的业务逻辑。数据对象是纯数据,没有行为。
适用于简单的业务逻辑。
业务逻辑由对象模型和相对较小的一些类的网络组成。这些类通常对应于问题域中的概念。有些类只有状态或行为,但大多数类是包含状态和行为。
服务类具有针对每个请求或系统操作的方法,但是服务方法通常很简单,总是调用领域对象,这些对象中包含大量的业务逻辑。
好处:易于理解和维护。它不是由一个完成的所有事情的大类来完成,而是由许多小类组成,每个不类都有少量职责。面向对象的设计更容易测试,更容易扩展。
子域概念有助于把应用程序分解为服务。
战略性模式:子域和相关联的限界上下文
战术性模式: