分布式事务解决方案-seata
什么是MySQL XA方案?
MySQL从5.7开始加入了分布式事务的支持。MySQL XA中拥有两种角色:RM,TM。
RM(Resource Manager):用于直接执行本地事务的提交和回滚。在分布式集群中,一台MySQL服务器就是一个RM。
TM(Transaction Manager):TM是分布式事务的核心管理者。事务管理器与每个RM进行通信,协调并完成分布式事务的处理。
发起一个分布式事务的MySQL客户端就是一个TM。
XA的两阶段提交分为Prepare阶段和Commit阶段,过程如下:
阶段一为准备(prepare)阶段:即所有的RM(Resource Manager)锁住需要的资源,在本地执行这个事务(执行sql,写redo/undo log等),但不提交,然后向TM(Transaction Manager)报告已准备就绪
阶段二为提交阶段(commit):当TM(Transaction Manager)确认所有参与者都ready后,向所有参与者发送commit命令。
什么是seata?
Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案。

MySQL XA与Seata(Fescar) AT的区别?

使用Seata解决分布式异常
-
模拟分布式事务异常
-
使用Seata解决
完成父工程:
1.新建springboot项目,删除src文件夹
2.导入pom依赖
<properties>
<java.version>1.8</java.version>
<!--spring cloud 版本-->
<spring-cloud.version>Hoxton.SR3</spring-cloud.version>
<spring-cloud-alibaba.version>2.2.1.RELEASE</spring-cloud-alibaba.version>
</properties>
<!--引入Spring Cloud 依赖-->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring-cloud-alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
完成子工程order(maven项目):
1.新建数据库pay,表名pay

2.导入maven依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
</dependencies>
3.编写application.yml
server:
port: 8020
spring:
application:
name: pay
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: root
url: jdbc:mysql://localhost:3306/pay?serverTimezone=GMT%2B8
4.编写启动类
@SpringBootApplication
@EnableDiscoveryClient
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class, args);
}
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
5.完成service
OrderService.java
public interface OrderService {
void save();
}
OrderServiceImpl.java
@Service
public class OrderServiceImpl implements OrderService {
@Autowired
private JdbcTemplate jdbcTemplate;
public void save(){
this.jdbcTemplate.update(" INSERT INTO `order` (`username`) VALUES ('zhangsan') ");
}
}
6.编写OrderController.java
@RestController
public class OrderController {
@Autowired
private OrderService orderService;
@Autowired
private RestTemplate restTemplate;
@GetMapping("/save")
public String save(){
//订单
this.orderService.save();
int i = 10/0;
//支付
//控制器 Order 通过 RestTemplate 调用 Pay 的服务
this.restTemplate.getForObject("http://localhost:8020/save",String.class);
return "success";
}
}
完成子工程pay(maven项目):
1.新建数据库pay,表名pay

2.导入maven依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
</dependencies>
3.编写application.yml
server:
port: 8020
spring:
application:
name: pay
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: root
url: jdbc:mysql://localhost:3306/pay?serverTimezone=GMT%2B8
4.编写启动类
@SpringBootApplication
@EnableDiscoveryClient
public class PayApplication {
public static void main(String[] args) {
SpringApplication.run(PayApplication.class, args);
}
}
5.完成service
PayService.java
public interface PayService {
void save();
}
PayServiceImpl.java
@Service
public class PayServiceImpl implements PayService {
@Autowired
private JdbcTemplate jdbcTemplate;
public void save(){
this.jdbcTemplate.update(" INSERT INTO `pay` (`username`) VALUES ('张三') ");
}
}
6.编写PayController.java
@RestController
public class PayController {
@Autowired
private PayService payService;
@GetMapping("/save")
public String save(){
this.payService.save();
return "success";
}
}
使用Seate解决分布式问题
1.下载Seate https://seata.io/zh-cn/blog/download.html
我用的版本0.9.0

2.解压后分别修改nacos-config.txt和registry.conf

regisry.conf
注意serverAddr地址如果是linux中,不在本机,需要写uri
registry {
type = "nacos"
nacos {
serverAddr = "localhost"
namespace = "public"
cluster = "default"
}
}
config {
type = "nacos"
nacos {
serverAddr = "localhost"
namespace = "public"
cluster = "default"
}
}
nacos-config.txt
名字与工程中application.yml中的name属性对应

transport.type=TCP transport.server=NIO transport.heartbeat=true transport.thread-factory.boss-thread-prefix=NettyBoss transport.thread-factory.worker-thread-prefix=NettyServerNIOWorker transport.thread-factory.server-executor-thread-prefix=NettyServerBizHandler transport.thread-factory.share-boss-worker=false transport.thread-factory.client-selector-thread-prefix=NettyClientSelector transport.thread-factory.client-selector-thread-size=1 transport.thread-factory.client-worker-thread-prefix=NettyClientWorkerThread transport.thread-factory.boss-thread-size=1 transport.thread-factory.worker-thread-size=8 transport.shutdown.wait=3 service.vgroup_mapping.pay=default service.vgroup_mapping.order=default service.enableDegrade=false service.disable=false service.max.commit.retry.timeout=-1 service.max.rollback.retry.timeout=-1 client.async.commit.buffer.limit=10000 client.lock.retry.internal=10 client.lock.retry.times=30 client.lock.retry.policy.branch-rollback-on-conflict=true client.table.meta.check.enable=true client.report.retry.count=5 client.tm.commit.retry.count=1 client.tm.rollback.retry.count=1 store.mode=file store.file.dir=file_store/data store.file.max-branch-session-size=16384 store.file.max-global-session-size=512 store.file.file-write-buffer-cache-size=16384 store.file.flush-disk-mode=async store.file.session.reload.read_size=100 store.db.datasource=dbcp store.db.db-type=mysql store.db.driver-class-name=com.mysql.jdbc.Driver store.db.url=jdbc:mysql://127.0.0.1:3306/seata?useUnicode=true store.db.user=mysql store.db.password=mysql store.db.min-conn=1 store.db.max-conn=3 store.db.global.table=global_table store.db.branch.table=branch_table store.db.query-limit=100 store.db.lock-table=lock_table recovery.committing-retry-period=1000 recovery.asyn-committing-retry-period=1000 recovery.rollbacking-retry-period=1000 recovery.timeout-retry-period=1000 transaction.undo.data.validation=true transaction.undo.log.serialization=jackson transaction.undo.log.save.days=7 transaction.undo.log.delete.period=86400000 transaction.undo.log.table=undo_log transport.serialization=seata transport.compressor=none metrics.enabled=false metrics.registry-type=compact metrics.exporter-list=prometheus metrics.exporter-prometheus-port=9898 support.spring.datasource.autoproxy=false
在路劲下的地址栏输入cmd,在cmd界面输入startup/startup.cmd
运行 nacos-config.sh 将 Seata 配置导入 Nacos
注意:windows中不能直接运行sh文件,可用git启动
# 进入conf目录,git hash中执行: sh nacos-config.sh 127.0.0.1
# 进入bin目录 ,地址栏输入cmd seata-server.bat -p 8090 -m file
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
OrderApplication.java
@SpringBootApplication
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class, args);
}
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
@Bean
public JdbcTemplate jdbcTemplate(DataSource dataSource){
return new JdbcTemplate(new DataSourceProxy(dataSource));
}
}
PayApplication.java
@SpringBootApplication
public class PayApplication {
public static void main(String[] args) {
SpringApplication.run(PayApplication.class, args);
}
@Bean
public JdbcTemplate jdbcTemplate(DataSource dataSource){
return new JdbcTemplate(new DataSourceProxy(dataSource));
}
}
spring:
application:
name: order
cloud:
nacos:
config:
server-addr: localhost:8848
namespace: public
group: SEATA_GROUP
alibaba:
seata:
tx-service-group: ${spring.application.name}
spring:
application:
name: pay
cloud:
nacos:
config:
server-addr: localhost:8848
namespace: public
group: SEATA_GROUP
alibaba:
seata:
tx-service-group: ${spring.application.name}
@RestController
public class OrderController {
@Autowired
private OrderService orderService;
@Autowired
private RestTemplate restTemplate;
@GetMapping("/save")
@GlobalTransactional
public String save(){
//订单
this.orderService.save();
int i = 10/0;
//支付
this.restTemplate.getForObject("http://localhost:8020/save",String.class);
return "success";
}
}
运行
在OrderController.java打断点

Debug运行Order
运行Pay
访问http://localhost:8010/save
查看order数据库
order表

undo_log表

直接运行完
order表

undo_log表


进行了数据的回滚,如果去掉模拟异常int i=10/0,两个数据库能够存入数据




浙公网安备 33010602011771号