传统意义上的事务被定义在数据层面,它是指一组原子操作,这组原子操作必须按照既定的顺序全部执行成功 。 如果某一个或者多个原子操作失败,则回退所有之前的原子操作到原来的状态 。
事务的特点主要有四个:原子性( Atomicity )、 一致性( Consistency )、隔离性( Isolation )和持久性( Durability )。
一个标准的事务必须同时满足这四个特性,否则就无法保持业务数据的正确性一一并且在 SQL 规范中也明确定义了这些特性的实现标准,例如它定义了四类事务隔离级别: Read Uncommitted (读未提交〉、 Read Committed (读己提交)、 Repeatable Read(可重复读 )和 Serializable ( 可串行化〉 。
传统业务环境下这些原子数据操作都在同一个数据库实例上完成 , 而随着企业中各系统的复杂度增加,就可能会出现事务跨两个或者多个系统数据库实例的情况 , 后一种事务处理机制就是常说的分布式事务。
1.分布式事务与两阶段提交协议(2PC)--作用于系统数据层
两阶段提交协议( 2PC ) 。 简单来说这个协议中提到两个阶段是指准备阶段和提交阶段
它的实现一般需要一个事务协调者来统一所有工作,协调者首先向参与事务动作的各个数据节点提交准备请求(也叫检查请求),并等待所有参与此事务的数据节点返回确认信息。当各个数据节点收到这个准备请求后就会检查自己是否有条件执行自己负责的处理部分,并且执行但不提交各自负责的这部分数据操作。这是一个同步过程,在收到所有节点确认信息之前协调者都不会发出下一步的执行指令, 各个节点被锁定/预占的资源也不会被释放一一因为各数据节点的处理过程都未正式提交。如果在这个过程中任何一个数据节点返回了“不可用”或者等待超时,那么协调者就会向各个参与事务动作的处理节点发出“回液”指令,各个处理节点就会 回滚/释放资源,并将回滚结果回馈给协调者 ;如果协调者收到所有处理节点“准备好”/“同意”的信号,那么就会进入第二个阶段一一通知各个处理节点“提交” 。当各个处理节点收到第二步指令后,就会正式提交上一步已经完成的处理动作,并向协调者回馈“完成”消息 。
分布式事务己经有非常多的实现组件和成功案例,例如第三方组件:
> JOTM (Java Open Transaction Manager)就是一个成熟的分布式事务管理器,现在许多 Tomcat 服务器上都是用它提供的分布式事务功能:
> Atomikos 也是一款常用的分布式事务管理器,也是目前配合Spring 集成的一款分布式事务管理器 。
2.关系型数据库的设计的 ACID 原理
即原子性( Atomicity )、 一致性( Consistency )、隔离性( Isolation )和持久性( Durability ),而且关系型数据库普遍采用事务技术实现 ACID原理,其中每一个事务就是最小的原子操作,还可以设置不同的事务级别,包括可读未提交、可重复读这样的事务级别:关系型数据库还规定了 一旦事务正确提交就不能进行数据回滚 , 如果要继续修改数据就只能启动一个新的事务 。 而这一切都是为了保证数据库系统是一个强一致性系统。 就连架设在各个关系型数据库实例之上的分布式事务机制,也是为了保证这个 目标。
只要有任何一个参与分布式事务过程的数据库实例出现异常,整个分布式事务就无法正常提交 ,当然也就无法完成数据写操作 。 注意,数据是可以在事务操作的同时进行读操作的,即使某些节点出现了 问题其他数据库节点也可以承担这个读操作 , 因为这样的操作不存在一致性改变的风险。
3.TCC (Try Commit Cancel)--作用于系统RPC层
TCC 过程实际上是 2PC 的一种变形,它与后者的关键区别在于 TCC 的思路着重于原子服务而非具体数据操作。 Try步骤的细分工作又包括业务检查和资源预占,而对于如何实现这个过程,在 TCC 中并没有明确要求必须通过对持久层数据进行写操作,这就便于架构师在服务层而非在数据层上设计 TCC 的实现。
4.CAP
分布式系统的知识体系中有一个非常重要的概念一一CAP 原理。这个原理指导着大多数分布式系统的设计过程 ,
CAP 原理大致是说分布式系统中一定存在三个特性 : 一 致性( Consistency )、分区容忍性( Partition )和可用性( Availability ) 。
HBase选择了C(一致性)与P(分区可容忍性),Cassandra选择了A(可用性)与P(分区可容忍性),HDFS 选择了P(分区可容忍性)与A(可用性)
分区性的要求:数据 X 至少也应该在不同的节点上存储多份(至少应该有三份),存储的副本量越多越能保证数据 X 的安全,也越能保证即使在多个节点同时不可用的情况下,数据 X 也同样能够被访问 。
一致性的要求:当客户端发出数据 X 的更新请求后, 从任何一个节点访问数据 X 都可以拿到它最新的状态。如果真要达到这么理论的一致性要求,那就只能让所有需要读/写数据 X的客户端等待 , 直到完成数据 X 的所有副本同步后,再依次进行响应 。 但这样做又不满足可用性要求。
可用性的要求:任何一个没有发生故障的节点必须在有限的时间内返回结果。然而,如果系统能够做到当某个节点发生网络分区后,将它从系统中剔除,由其它节点继续提供服务。虽然没有满足CAP中A的要求,但是,只要恢复时间足够快,也符合高可用的要求。
5.BASE
BASE ,即 基本可用 ( Basically Availble )、软状态( Soft State )和最终一致( Eventual Consistency ) ,
任何分布式系统都不可能以 CAP 原理中三个特性同时作为设计目标,没有任何分区容忍性的分布式系统甚至都不能称为分布式系统。
一致性的概念:强一致性和最终一致性 。
强一致性可以概括为任何时刻客户在分布式系统中获取数据 X,无论它在分布式系统的哪个节点进行这个操作,其获取到的数据 X 都是一致的 。 从这个定义来看 ,分布式事务机制就是一种强一致性的实现 。
弱一致性不是说不保持数据的一致性,而是说不保证数据每时每刻都一致,也不承诺什么 时候才能保证分布式系统的任何节点都能读取到一致的数据。而最终一致性是弱一致性的一种特定结果, 即是承诺基于弱一致性,在经过一个数据不一致的时间窗口后,最终能保证数据一致 。 这个数据不一致的时间窗口在客户端看来非常短,而且分布式系统还可以通过多种方式向客户端屏蔽不一致的数据,例
如主从副本方式 。
BASE 的核心思路是在保证可用性和分区容忍性的前提下,找到一个牺牲一致性的程度,而最终一致性为我们指明了 这个程度一一保证数据最终一致即可。那么对于跨系统分布式业务我们也可以借鉴这个概念: 各个原子服务无须关心其他原子服务什么时候执行或者执行顺序,只需要各自负责自己的那部分业务,并最终完成处理即可。如果出现需要回退的情况,则各个原子服务负责取消自己负责的那部分操作 , 回到原始状态 。
6.事务补偿机制
这种机制本身并不提供事务,而是在需要进行回溶操作时能够依据某种手段获知整个执行路径,并完成符合业务规则的逆向执行动作。为了保证事务补偿机制能够运行,业务系统提供了某个原子服务就必须提供一个和这个原子服务反向操作的另一个原子服务,这样才可以保证事务补偿机制在任何一个正 向执行的分布式业务工作的环境中,都有一个与之对应的反向执行过程。
当一个原子服务被执行时数据即时被更改,占用资源使用后即时被释放,执行日志被详细记录。如果某一个原子服务执行失败并不是将之前未提交数据回滚,而是通过一个对应的反向服务将之前的结果逆向执行,这里的逆向执行将重新修改数据 、 重新占用资源、重新生成新的日志。根据各个原子服务间的依赖性,这个执行过程既可以是顺序执行的又可以是并行执行的。
事务补偿机制可以通过多种方式进行实现:
1.独立实现一个事务补偿控制模块,井向这个模块注册每一个正向方法和对应的逆向方法,跨系统业务由这个模块负责执行井在出现执行错误的情况下进行重试或者逆向执行:
2.采用日志方式进行实现,将跨系统业务的发起方和各个执行方执行的每一步写入日志,并在出现执行错误时利用日志记录的信息分析和执行回滚策略,最后还要通过日志记录整个业务是否达到了最终一致性。
应用场景:
事务补偿机制可以解决长耗时业务过程中资源占用的问题,但是还是不能有效缩短业务执行时间 。因为在每个原子服务执行完成后资源就被释放了,而不是像两阶段提交协议那样在整个业务执行完成前都需要独占资源。
事务补偿机制虽然部分解决了分布式事务在业务系统的适用度问题,但是它本身也不是完美的。例如它不适用需要保证实时一致性的业务场景,
6.可靠的异步消息
可靠的异步消息系统是目前解决这类问题(电商订单涉及多个子系统调用的问题)所常用的方式,
“可靠”的定义是消息系统不会无故丢失消息,保证消息送达到指定的目标系统:
“异步”是指消息的发送方(又称生产方)在迭出消息后 ,无须等待消息接收方(又称消费方)反馈任何结果即可执行后续的操作。
典型的可靠的异步消息系统有 ActiveMQ 、 RabbitMQ 和 RocketMQ。
7.为保持最终一致性的事务补偿机制,或者又是可异步工作的可靠消息系统,在设计和使用时都需要注意问题
1)注意重试操作:由于分布式业务的各个执行步骤存在于不同的系统中,这些系统依靠网络进行互相通信,所以在服务的调用过程中不可能保证 100%成功 。 也就是说即使每个原子服务工作正常,整个分布式业务的工作过程也可能因为某些工作环境原因而调用失败,所以一旦某个原子服务调用失败,协调者要做的事情并不是立即进行业务回滚、逆向操作或者异常消息通知,而应该首先进行重试操作,这是为了排除包括网络抖动、主从服务切换在内的服务临时不可用情况 。经过一定数量的重试后,如果服务还是不可用,则协调者再进行业务回液、逆向操作或者异常消息通知 。 以上示例代码的客户端并没有任何的重试过程,就开始进行逆向操作了 。
2)注意冪等性:所谓冪等性是指参与同 一个分布式业务的各个操作步骤,无论执行多少次操作动作其结果都与执行一次操作动作后的结果一致。实际上这是对重试操作特性的一种支持,由于重试操作在异常情况下进行,所以谁也不能保证原子服务不会被错误地多次调用,甚至不能保证请求发起者不会错误地重复发起同一个分布式业务操作过程 。 例如,订单生成时要避免重复生成的情况发生、订单执行过程要防止出 现重复生成配送单的情况、订单逆向执行的退款步骤要防止重复退款的情况发生。
3)注意基于日志:日志的作用可概括为事中记录和事后回溯, 一个分布式服务过程的每一个执行步骤都应该详细记录日志,无论这个过程是正向执行还是逆向执行; 在分布式服务执行完成后也应该记录日志,无论这个分布式服务执行是否成功 。这些日志可以在分布式业务执行结束后检查最后的执行效果,在协调器出现窑机时利用日 志恢复协调器执行状态保持执行过程的一致性,在需要进行逆向执行时也可利用日志找到对应的回溯路径。以之前的示例代码来说 , Invoker 角色中的代码虽然可以在某个原子服务出现异常时,按照分布式业务对应的逆向过程进行执行,但是却没有使用任何办法保证逆向过程执行的可靠性一一没有记录日志 。 所以要是某个原子服务执行的逆向过程失败,整个系统的数据一致性就没有保证了。
4)参与分布式服务的各个原子服务,在没有业务间依赖的情况下可以采用并行执行的方式缩短整个分布式服务的执行时间 ,提高执行效率。
8.Hystrix与熔断机制
通过多种手段帮助技术人员解决系统间调用时的错误,防止系统雪崩效应。Hystrix 将分布式业务的正向操作和逆向操作分离开,并通过设置最长等待时间、抛出异常等方式让逆向操作自动执行。
关键点
1)首先 Hystrix 的单次执行是以一个命令完成的,其内是一个完整的命令模式,其外向开发人员暴露的是一个命令元素(Command 角色,其实现为HystrixCommand<R> 类或 HystrixExecutable<R>接口)。
开发人员需要实现其中的 run()方法与getFallback()方法
2)Hystrix 将具体的任务(一个 Command 角色)放入线程池中执行,而且 Hystrix 中不但可以管理 一 个线程池,还可以为不同的 Command 设置不同的ThreadPoo!Key , 让它们工作在不同 的线程池中
3)Hystrix 对 Command 的执行分为三种方式,同步执行 execute()、异步执行 queue()和带有观察器的执行方式 observe()
9.系统间服务调用的问题
1)访问权限问题:
在整个系统生态环境中,不是任何用户都可以随意访问任意业务服务接口的 。 除了访问接口的用户组、用户和密码管理(或者是公私钥文件),还需要限制用户的访问权限。
2)版本控制问题:
为了子系统能够保证 24 小时连续提供服务,就需要标注服务接口的版本号,让之前没有完成升级的服务节点提供/访问老版本的服务接口:己经完成升级的服务节点提供新版本的服务接口。
3)服务时效、次数控制问题:
各个子系统提供的服务本身也是具有时效性的。比如某一个服务 Y 只在每天早上 10 : 00~11 :00 才能提供访问调用,且对于某个用户来说每天只能调用 100 次。
4)性能措施问题:
系统服务接口能够承受多大的 TPS 是衡量其性能的一个重要指标 。 但是在生产环境下,往往再高的系统接口都会遇到 TPS 瓶颈。在此种情况下,我们一般会准备备用手段对请求进行导流,
5)跨平台性问题:
在有历史遗留情况的系统中,或者有多个技术背景不同的团队同时进行研发的大中型业务系统中,这就要求远程业务接口能够支持多种语言客户端的调用 。 目前对这类问题的解决方法无外乎几种:
(1)一种是使用各开发语言都更容易理解和管理的基于网络应用层协议的接口调用方式,例如 HTTP RESTful 形式的服务接口:
(2)一种是基于某种支持跨语言特性的RPC 组件提供接口支持,例如基于 Apache Thrift提供的服务接口 :
(3)还有一种是代理装置,通过这个代理装置,将某种语言某种消息格式的调用转换为另 一种语言另一种消息格式的调用,而 ESB 技术就类似于这样一种代理装置。
10.SOA架构
SOA ( Service-Oriented Architecture ) 中文全称 “面向服务的架构” 。 SOA 主要围绕多个 “服务”如何进行集成以达到某种服务目的进行讨论 。
服务: 在业务系统中被发布出来供用户使用,并能够完成一个完整业务过程的功能。
服务着眼于完整的业务:服务的定义对象是业务系统中的完整业务功能。
服务的粒度虽然相对粗放,但却是可控的:服务拆分的目标是保留重用度:务粒度的拆分完全依据业务系统中业务过程进行定义和分析
服务集成的目的是形成一个新的服务:对企业内部(或者企业间〉的业务服务进行集成,被集成的业务服务称之为原子服务,集成的目 的是重用这些原子服务形成一个新的服务。
SOA 需保证屏蔽细节:从技术细节层面看,开发语言、外传输协议、消息格式都可以使用 SOA 进行集成与转换;从业务细节层面看, 第三方系统只需要知道调用某一个服务就可以达到业务目的,至于提供服务的业务系统如何实现业务过程则无须关心。
SOA 让各业务系统保持松散:通过屏蔽各业务系统技术细节和业务细节,兼容各业务系统的不同传输协议和不同消息格式,可以让通过 SOA进行业务集成的,各个业务系统保持低祸合状态。
11.ESB
SOA 架构模型是解决问题的纲领性思路,在这个规则下ESB ( Enterprise Service Bus ,企业服务总线〉是其一种具体办法。
为了满足 SOA 架构思想的设计要点,达到既定的工作目标, ESB 总钱技术至少需要帮助这些业务系统完成以下工作。
· 多种调用协议的兼容支撑和转换
· 多种消息格式兼容支撑和转换
· 服务监控管理(注册、安全、版本、优先级):首先业务系统提供的服务可能会以-定周期发生变化,ESB能够保证在这样的情况下集成服务依然可以工作;其次ESB 应该有一套完整的功能来保证服务集成的安全性和权限 。
· 服务集成和编排:服务编排的作用就是明确原子服务执行的先后顺序、判断原子服务执行的条件、确保集成后的新服务能够按照业务设计者的要求正常工作 。
常见的 ESB 产品
1.IBM 提供了两款纯软件的 ESB 产品 : IBM WebSphere ESB 和 IBM WebSphere MessageBroker(WMB〉
2.Oracle Service Bus ( OSB )是 Oracle 的付费产品,和 WMB 类似, OSB 也是全分布式的ESB 产品
3.普元 ESB 产品是国内比较有代表性的拥有自 主产权的 ESB 产品 。
4.Mule ESB 是服务提供商 MuleSoft的产品
5.Apache Camel 是 Apache 基金会下的一个顶级开源项目,它提供了一套消息路由规则、消息转换引擎和服务编排等能力。 不过它并没有现成的消息转换组件
12.服务治理框架
ESB 企业总线的存在目的之一是满足企业建设过程中新系统和遗留系统的集成问题,所以ESB 中的一个功能侧重点就是协议转换和信息转发
服务治理框架并不进行原子服务的真实调用,而只记录原子服务的调用地址、权限、熔断方案、备用路径等信息,井在各原子服务调用时监控实时压力 。当D 系统需要调用外围系统时,首先会请求服务治理框架拿到真实的调用路径 , 再自行进行调用。
降低了服务调用的跨平台能力、服务编排能力和对遗留系统的集成能力,但是显著提高了各系统间的调用吞吐量以及各系统集成到服务治理框架上的难度,非常适合互联网系统对于轻量化和快速部署的要求。
常见的服务治理框架软件
1. 阿里 DUBBO
2. Spring Cloud(Config、Eureka、Hystrix、Zuul、Ribbon等)