在微服务环境下,会根据不同的业务会拆分成不同的服务,每个服务都有自己独立的数据库,服务与服务之间采用RPC远程调用进行通信,但在每个服务中都有自己独立的本地事务。当服务相互通讯的时候,两个本地事务互不影响,从而需要分布式事务。
关系型数据库天生就是解决具有复杂事务场景的问题,关系型数据库完全满足ACID的特性。
数据库管理系统中事务的四个特性
所谓事务,它是一个操作序列,这些操作要么都执行,要么都不执行,它是一个不可分割的工作单位。(执行单个逻辑功能的一组指令或操作称为事务)
BASE 是 Basically Available(基本可用)、Soft state(软状态)和 Eventually consistent(最终一致性)三个短语的简写,由 eBay 架构师 Dan Pritchett 于 2008 年在《BASE: An Acid Alternative》(论文地址点 这里)论文中首次提出。BASE 思想与 ACID 原理截然不同,它满足 CAP 原理,通过牺牲强一致性获得可用性, 一般应用于服务化系统的应用层或者大数据处理系统中,通过达到最终一致性来尽量满足业务的绝大多数需求。
BASE 模型包含如下三个元素:
关于最终一致的几种变种参见上面,在实际系统实践中,可以将若干变种结合起来,来实现各种业务需求。
由于对系统或者数据进行了拆分,我们的系统不再是单机系统,而是分布式系统,针对分布式系统的CAP原理包含如下三个元素。
C:Consistency,致性。在分布式系统中的所有数据 备份,在同一时刻具有同样的值,所有节点在同一时刻读取的数据都是最新的数据副本。
A:Availability,可用性,好的响应性能。完全的可用性指的是在任何故障模型下,服务都会在有限的时间内处理完成并进行响应。
P: Partition tolerance,分区容忍性。尽管网络上有部分消息丢失,但系统仍然可继续工作。
CAP原理证明,任何分布式系统只可同时满足以上两点,无法三者兼顾。由于关系型数据库是单节点无复制的,因此不具有分区容忍性,但是具有一致性和可用性,而分布式的服务化系统都需要满足分区容忍性,那么我们必须在一致性和可用性之间进行权衡。如果在网络上有消息丢失,也就是出现了网络分区,则复制操作可能会被延后,如果这时我们的使用方等待复制完成再返回,则可能导致在有限时间内无法返回,就失去了可用性:而如果使用方不等待复制完成,而在主分片写完后直接返回,则具有了可用性,但是失去了一致性。
XA是由X/Open组织提出的分布式事务的规范。XA规范主要定义了(全局)事务管理器(Transaction Manager)和(局部)资源管理器(Resource Manager)之间的接口。XA接口是双向的系统接口,在事务管理器(Transaction Manager)以及一个或多个资源管理器(Resource Manager)之间形成通信桥梁。XA之所以需要引入事务管理器是因为,在分布式系统中,从理论上讲(参考Fischer等的论文),两台机器理论上无法达到一致的状态,需要引入一个单点进行协调。事务管理器控制着全局事务,管理事务生命周期,并协调资源。资源管理器负责控制和管理实际资源(如数据库或JMS队列)
作为java平台上事务规范JTA(Java Transaction API)也定义了对XA事务的支持,实际上,JTA是基于XA架构上建模的,在JTA 中,事务管理器抽象为javax.transaction.TransactionManager接口,并通过底层事务服务(即JTS)实现。像很多其他的java规范一样,JTA仅仅定义了接口,具体的实现则是由供应商(如J2EE厂商)负责提供,目前JTA的实现主要由以下几种:
LCN的官网:github
https://github.com/codingapi/tx-lcn
这里使用tx-lcn-2.0
首先编译tx-lcn 的环境,用mvn 将tx-clinet、tx-lcn、tx-plugins install到自己maven环境中。
然后去tx-manage下修改配制文件中的Eureka的地址,和redis的地址。
如果这里有不明白SpringCloud微服务搭建的,可以去我博客找 “SpringCloud Eureka做服务治理和 Eureka注册中心集群搭建 及 自我保护机制” 这篇文档参考。
先启动eureka注册中心,再启动TxManager,启动成功后可再浏览器输入:
http://localhost:8899/
如果有如下效果则TxManager启动成功。
注意,这里LCN底层通信采用的端口为 9999,如果部署在CenterOS下,需要开放该端口。
现在又两个数据库Db1,Db2,分别为服务提供者,和消费者使用。这里关于MybatisPlus数据库的配制和操作不进行展示了。
com.codingapi
transaction-springcloud
4.1.2
org.slf4j
*
com.codingapi
tx-plugins-db
4.1.2
org.slf4j
*
#LCN
tm.manager.url = http://127.0.0.1:8899/tx/manager/
@Service
public class TxManagerTxUrlServiceImpl implements TxManagerTxUrlService {
@Value("${tm.manager.url}")
private String url;
@Override
public String getTxUrl() {
System.out.println("load tm.manager.url ");
return url;
}
}
@Service
public class TxManagerHttpRequestServiceImpl implements TxManagerHttpRequestService {
@Override
public String httpGet(String url) {
System.out.println("httpGet-start");
String res = HttpUtils.get(url);
System.out.println("httpGet-end");
return res;
}
@Override
public String httpPost(String url, String params) {
System.out.println("httpPost-start");
String res = HttpUtils.post(url, params);
System.out.println("httpPost-end");
return res;
}
}
在需要使用分布式事物的方法上添加注解
@TxTransaction(isStart = false)
其中isStart =true的话代表发起方,false代表接收方。
和服务提供者配制一样,在调用服务时,注解的isStart设为true:
@TxTransaction(isStart = true)