在spring cloud技术栈中分布式事务也是比较重要的一环,分布式事务的处理关系到了数据的一致性和完整性,所以微服务的开发分布式事务是必不可少的,这也对开发人员也是不小的挑战,好在市面上不缺分布式事务的解决方案,这些方案也各有优劣,本次将把seata作为分布式事务的解决方案来进行代码实现,在这之前我特意找了点资料给大家参考
分布式事务的三种解决方案: https://www.cnblogs.com/bluemiaomiao/p/11216380.html
seata原理解析:https://www.jianshu.com/p/044e95223a17
github相关资料:https://github.com/seata/seata-samples/tree/master/springcloud-nacos-seata
1 seata服务端配置
这一步在上面提供的第三个参考资料里有写,对应第一个步骤,只要按照上面写的步骤基本就没问题了,容易出问题的地方是1.3的那一步
1.3 启动seata-server
分两步,如下
# 初始化seata 的nacos配置 cd conf sh nacos-config.sh 192.168.21.89 # 启动seata-server cd bin sh seata-server.sh -p 8091 -m file
linux上运行seata倒也还好,但如果是windows系统显然就不能用sh命令了,好在还提供了其他方式,seata的nacos配置可以借助python ,seata的启动倒是直接改成bat的方式并加上参数就ok了,这两步的命令改成:
py nacos-config.py nacos的IP 即可(需要安装python)
seata-server.bat -p 8091 -m file
这一步没问题了才能保证后面程序的正常运行
2 在我们的数据库中创建undo_log
CREATE TABLE `undo_log`
(
`id` BIGINT(20) NOT NULL AUTO_INCREMENT,
`branch_id` BIGINT(20) NOT NULL,
`xid` VARCHAR(100) NOT NULL,
`context` VARCHAR(128) NOT NULL,
`rollback_info` LONGBLOB NOT NULL,
`log_status` INT(11) NOT NULL,
`log_created` DATETIME NOT NULL,
`log_modified` DATETIME NOT NULL,
`ext` VARCHAR(100) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `ux_undo_log` (`xid`, `branch_id`)
) ENGINE = InnoDB
AUTO_INCREMENT = 1
DEFAULT CHARSET = utf8;
建表语句摘抄至https://github.com/seata/seata-samples/tree/master/springcloud-nacos-seata
如果有n个微服务且每个微服务使用的数据库不是同一个且这些微服务都需要进行分布式事务,那么就把这个表建到每一个数据库里即可
3 微服务项目中引入依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
</dependency>
4 在项目的resources目录添加registry.conf文件(直接粘贴前面配置好的)和bootstrap.yaml文件
registry {
# file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
type = "nacos"
nacos {
serverAddr = "localhost"
namespace = "public"
cluster = "default"
}
}
config {
# file、nacos 、apollo、zk、consul、etcd3
type = "nacos"
nacos {
serverAddr = "localhost"
namespace = "public"
}
}
yaml可以像下面这样写:
spring:
application:
name: order-service
cloud:
nacos:
config:
server-addr: localhost:8848
namespace: public
group: SEATA_GROUP
alibaba:
seata:
tx-service-group: ${spring.application.name}
namespace和group不能乱写,最重要的是tx-service-group配置,这里就和nacos-config.txt里写成一样
service.vgroup_mapping.order-service=default
service.vgroup_mapping.member-service=default
就是里面的order-service和member-service了,配置写错了分布式事务就不会生效了
5 代码编写
(1) 在分布式事务的起点位置的方法加 @GlobalTransactional 注解
@GlobalTransactional
public void createOrder(Order order, String txId) {
orderDao.save(order);
feignMemberService.memberAddPoint(1, 50);
}
此处的逻辑是生成订单之后,通过Feign进行服务调用,然后给用户进行加积分操作,模拟了分布式事务
(2) 分布式事务的其他被调用代码编写
@Transactional
public void memberAddPoint(Integer id, Integer addPoint) {
Member m = memberDao.findById(id).get();
int point = m.getPoint();
m.setPoint(point + addPoint);
int a=5/0;
memberDao.save(m);
}
接下来在controller写点代码就能测试了
@GetMapping("/createProdutOrder")
@ResponseBody
public String createProdutOrder() {
Order order = new Order();
order.setCreateTime(new Date());
order.setMemberId(1);
order.setOrderNo("RXE777777");
order.setTotalPrice(50.00);
orderService.createOrder(order, "");
return "*****OK*****";
}
这里需要加一个@Transactional注解 ,为了演示效果故意写一个by zero的异常模拟其他服务调用出现问题
6 效果演示
从控制台我们可以看到出现异常后事务回滚了,拿掉by zero的相关代码则事务正常提交
下面是controller被请求之后的返回
到此,就已经使用seata实现了分布式事务的控制