base是对ap的扩展的。
分布式事务的问题,张三给李四付款,李四消费了,但是没有返回,则张三回滚,此时会出现事务问题。
cap的理论:
结合主从数据库的读取。
c一致性:指的是写操作后,读的操作可以读到最新的状态。当数据分布在多个节点,任意节点都是最新的数据。一般写是主数据库,读是从数据库。
a:可用性:任何事务操作都可以得到相应的结果,且不会出现超时或者响应错误。从数据库及时得到结果不允许超时或者错误。
p分区容忍性:异步同步到从数据库,添加从数据库节点。
------------------------------------------------------------------------02---08-----------------------------------------------------------------------------------
两阶段提交:2pc
解决方案:XA 事务的管理器,事务的参与者
问题:依赖数据库,准备阶段锁定资源,要等到都好了才会释放,是在数据库实现的。
解决方案:Seata
问题:不要求数据库支持XA协议,工作在业务层,XA基于数据库的。对业务是0侵入的。
支持:AT和TCC。
RM是数据库的实例。
和XA不同,第一阶段就把锁释放了,并提交本地事务。不依赖数据库就行回滚而是依赖应用层。
回滚就是业务的反向操作。
TC:事务协调器,维护全局事务的运行状态,接受TM指令,发起全局事务的提交与回滚,负责与RM通信。
TM:事务管理器,负责开启全局事务,向TC发起全局提交或者回滚。
RM:数据库实例
AP:略。
工作过程:向TC注册,TC发事务ID,TM通知TC回滚还是提交。
----------------------------------------09--10------------------------------------------------------------------------------------
代码:
TM和RM是jar包的。
表:
要点说明:
构建工程:
第一步导入sql
表的结构:
四个表:account_info*2 undo_log*2
数据库:192.168.244.130/131 bank1 bank2 root 123456
第二步:Seata服务端
打开:
第三步:打开eureka的注册中心
第四步:打开eureka
第五步:导入dtx把bank1和bank2
第六步:分析提交
1.TM和RM向TC注册
2.在哪里开启全局事务TM就在哪力
标识开启全局事务的起点。
3.TC返回全局事务的ID
4.RM向TC注册分支事务,分支事务的ID和全局事务的ID已经绑定了
5.写扣减逻辑。
6.向undo写入数据代理的。
7.提交事务。
8.上报事务的处理结果。
9.通过feign接口携带事务ID去增加金额。
10.向TC注册分支事务。
11.
12.
第七步:分析回滚
-------------------------------------------------------11--------12------------------------------------------------------------------
分析一些配置:
第一步:
引入这个TM和RM就有了
第二步:拷贝文件,我们在TC事务协调器拷贝这两个文件
到:
第三步:
file.conf的分组的说明,我们是文件的不是数据库的就修改这个:
service {
#vgroup->rgroup
vgroup_mapping.seata-demo-bank1-fescar-service-group = "default"
#only support single node
default.grouplist = "127.0.0.1:8888"
#degrade current not support
enableDegrade = false
#disable
disable = false
}
修改1就行,2是固定的。
第一个名字就是微服务的注册的名字。这个是十分重要的。
default:TCC事务协调的默认的名字
第四步:修改配置类,配置代理数据源,Rm通过代理数据源在事务提交的时候,与TC通信交流,记录undo_log日志等。
@Configuration
public class DatabaseConfiguration {
private final ApplicationContext applicationContext;
public DatabaseConfiguration(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
@Bean
@ConfigurationProperties(prefix = "spring.datasource.ds0")
public DruidDataSource ds0() {
DruidDataSource druidDataSource = new DruidDataSource();
return druidDataSource;
}
@Primary
@Bean
public DataSource dataSource(DruidDataSource ds0) {
DataSourceProxy pds0 = new DataSourceProxy(ds0);
return pds0;
}
}
我们根据这个去配置去yml文件查看下。
---------------------------------------------------------------------13-----------------------------------------------------------------
案例是张三给李四转账:
第一步:写DAO
第二步:写Service
/**
* @author Administrator
* @version 1.0
**/
@Service
@Slf4j
public class AccountInfoServiceImpl implements AccountInfoService {
@Autowired
AccountInfoDao accountInfoDao;
@Autowired
Bank2Client bank2Client;
@Transactional //开启本地事务
@GlobalTransactional//开启全局事务 只开启一次
@Override
public void updateAccountBalance(String accountNo, Double amount) {
log.info("bank1 service begin,XID:{}", RootContext.getXID());
//扣减张三的金额
accountInfoDao.updateAccountBalance(accountNo,amount *-1);
//调用李四微服务,转账
String transfer = bank2Client.transfer(amount);
if("fallback".equals(transfer)){
//调用李四微服务异常
throw new RuntimeException("调用李四微服务异常");
}
if(amount == 2){
//人为制造异常
throw new RuntimeException("bank1 make exception..");
}
}
}
第三步:写Feign
@FeignClient(value="seata-demo-bank2",fallback=Bank2ClientFallback.class)
public interface Bank2Client {
//远程调用李四的微服务
@GetMapping("/bank2/transfer")
public String transfer(@RequestParam("amount") Double amount);
}
第四步:写controller。
---------------------------------------------------------------------14-----------------------------------------------------------------
李四的微服务:
@Transactional
@Override
public void updateAccountBalance(String accountNo, Double amount) {
log.info("bank2 service begin,XID:{}",RootContext.getXID());
//李四增加金额
accountInfoDao.updateAccountBalance(accountNo,amount);
if(amount==3){
//人为制造异常
throw new RuntimeException("bank2 make exception..");
}
}
---------------------------------------------------------------------15-----------------------------------------------------------------
测试:
第一步:启动三个工程,启动TC 协调器。
第二步:测试
1.正常的时候:http://localhost:56081/bank1/transfer?amount=1
2.给李四转的是3元,但是李四是异常的。
通过上下文拿到事务的id。
李四失败了张三回滚,张三失败了李四回滚。
在都未处理完成之后undolog日志会记录更新前和更新后的账户的信息的。
抛异常后自动生成update语句进行回滚。
3.超时:也会回滚
注意一点:每个数据库都要有undo_log这个表是本地事务一致性的关键,因为本地事务执行完都要提交的。
---------------------------------------------------------------------16-----------------------------------------------------------------
undolog日志,记录更新前的数据和更新后的数据。根据undolog日志自动生成update语句进行更新。