微服务中不可避免的会发生服务间的调用,这就一定会涉及到事务相关的问题,在单体项目中我们可以直接很方便的实现事务回滚,但是在分布式系统中就不能像以前那么做了,因为各个服务是独立的一套系统; 而要实现跨服务的事务管理系统的复杂度必然会大大增加,因此我们应当尽可能的避免使用分布式事务;对于那种要求不是很严格的可以考虑忽略掉事务的问题,只对重要的数据才做分布式事务。下面我们使用spring-cloud-alibaba套件Seata来实现分布式事务的功能。

Seata简介

Seata 是一款开源的分布式事务解决方案,致力于在微服务架构下提供高性能和简单易用的分布式事务服务。在 Seata 开源之前,Seata 对应的内部版本在阿里经济体内部一直扮演着分布式一致性中间件的角色,帮助经济体平稳的度过历年的双11,对各BU业务进行了有力的支撑。

相关文档

功能特性如下

  • 微服务框架支持:目前已支持 Dubbo、Spring Cloud、Sofa-RPC、Motan 和 grpc 等RPC框架,其他框架持续集成中;
  • TCC模式:支持 TCC 模式并可与 AT 混用;
  • XA模式:支持已实现 XA 接口的数据库的 XA 模式;
  • AT模式:提供无侵入自动补偿的事务模式,目前已支持 MySQL、 Oracle 、PostgreSQL和 TiDB的AT模式,H2 开发中;
  • 高可用:支持基于数据库存储的集群模式,水平扩展能力强;
  • SAGA模式:为长事务提供有效的解决方案;

Seata的使用

Seata支持本地文件模式和远程配置中心模式,下面我们分别介绍相关的使用方式。注意示例中使用的是spring-cloud-alibaba的套件;下面是代码示例:

使用file模式

添加maven依赖:

<!-- seata-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
</dependency>

spring-cloud-starter-alibaba-seata这个依赖中只依赖了spring-cloud-alibaba-seata,所以在项目中添加spring-cloud-starter-alibaba-seata和spring-cloud-alibaba-seata是一样的

seata配置文件

seata的配置参数官方文档 https://seata.io/zh-cn/docs/user/configurations.html

在application.yml里面配置seata需要的信息

spring:
cloud:
alibaba:
seata:
# 这里定义seata服务分组名称,必须和下面的seata.service.vgroup-mapping对应,否则将无法获取seata服务端IP信息
tx-service-group: seata-dubbo-b-seata-service-group
seata:
registry:
type: file
service:
# seata服务端的地址和端口信息,多个使用英文分号分隔
grouplist:
default: 192.168.56.101:8091
vgroup-mapping:
seata-dubbo-b-seata-service-group: default

上面的配置可以去看 io.seata.spring.boot.autoconfigure.properties.client.ServicePropertiesio.seata.discovery.registry.FileRegistryServiceImpl 这2个类你就明白了为啥这样配置了。

创建数据库表

在每一个业务库里面创建一个undo_log的表,这里表里面会记录事务信息,用于seata后面回滚数据使用。

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

开启全局事务

在需要开启全局事务的方法上添加 @GlobalTransactional 注解即可;只需要在起始的调用方法上加即可;注意对应异常情况想要回滚,直接抛出异常即可,否则将无法触发全局事务的回滚。 代码示例如下:

服务A

@Service
public class OrderServiceImpl implements OrderService { @Autowired
private OrderMapper orderMapper; @DubboReference(interfaceClass = GoodsService.class, check = false)
private GoodsService goodsService;
/**
* 预定
*/
@GlobalTransactional
@Override
public String booking(Long goodsId, Integer num) throws SQLException { Order order = new Order();
order.setOrderNo(String.valueOf(System.currentTimeMillis()));
order.setUid(1L);
order.setGoodsId(goodsId);
order.setIntegral(num*50); int count = orderMapper.insert(order);
if (count!=1){
System.out.println("订单创建失败");
return "订单创建失败";
}
boolean res = this.goodsService.deductInventory(goodsId, num);
if(!res){
throw new SQLException("库存不足");
} return order.getOrderNo();
}
}

服务B

@DubboService
public class GoodsServiceImpl implements GoodsService { @Autowired
private GoodsMapper goodsMapper;
@DubboReference(interfaceClass = IntegralService.class, check = false)
private IntegralService integralService; @Override
public boolean deductInventory(Long id, int num) throws SQLException {
Goods goods = goodsMapper.selectById(id);
int count = goodsMapper.deductInventory(id, num);
if (count!=1){
throw new SQLException("库存不足");
}
boolean res = this.integralService.deductIntegral(id, num*goods.getIntegral());
System.out.println("积分扣除结果:"+res);
if(!res){
throw new SQLException("积分不足");
}
return true;
}
}

服务C

@DubboService
public class IntegralServiceImpl implements IntegralService {
@Autowired
private MemberMapper memberMapper; @Override
public boolean deductIntegral(Long id, int integral) {
int count = memberMapper.deductIntegral(id, integral);
return count==1;
}
}

使用注册中心nacos进行集群

之前我们的seata是没有集群的,要集群的话那么就不能使用文件模式了,这里我们使用nacos来实现seata集群间的通信;注意这里使用的是nacos-1.x,在实际测试中使用nacos-2.x的时候会偶发出现dubbo服务无法调用的问题。

修改application.yml的配置,将上面seata部分的配置改为如下所示:

seata:
registry:
type: nacos
nacos:
serverAddr: 192.168.56.1:8848
application: seata-server
group: SEATA_GROUP
service:
vgroup-mapping:
# 这个必须和上面的匹配,同时最大长度为32;否则需要修改创建seata库中的global_table表的transaction_service_group的长度限制
seata-dubbo-b-seata-service-group: default

其他的无需改动;直接即可使用;服务启动成功后,seata服务那边也会打印相关信息。最后不得不吐槽下加入分布式事务组件后系统的响应就变慢,因此不到万不得已最好不用分布式事务,哪怕是通过后期手动处理。

Spring-Cloud-Alibaba之Seata的更多相关文章

  1. Spring Cloud Alibaba 使用Seata解决分布式事务

    为什么会产生分布式事务? 随着业务的快速发展,网站系统往往由单体架构逐渐演变为分布式.微服务架构,而对于数据库则由单机数据库架构向分布式数据库架构转变.此时,我们会将一个大的应用系统拆分为多个可以独立 ...

  2. Spring Cloud Alibaba 介绍及工程准备

    简介 SpringCloud Alibaba是阿里巴巴集团开源的一套微服务架构解决方案. 微服务架构是为了更好的分布式系统开发,将一个应用拆分成多个子应用,每一个服务都是可以独立运行的子工程.其中涵盖 ...

  3. Spring Cloud Alibaba | 微服务分布式事务之Seata

    Spring Cloud Alibaba | 微服务分布式事务之Seata 本篇实战所使用Spring有关版本: SpringBoot:2.1.7.RELEASE Spring Cloud:Green ...

  4. Spring Cloud Alibaba 初体验(六) Seata 及结合 MyBatis 与 MyBatis-Plus 的使用

    一.下载与运行 本文使用 Seata 1.1.0:https://github.com/seata/seata/releases Windows 环境下双击 bin/seata-server.bat ...

  5. Spring Cloud Alibaba 新版本发布:众多期待内容整合打包加入!

    在Nacos 1.0.0 Release之后,Spring Cloud Alibaba也终于发布了最新的版本.该版本距离上一次发布,过去了整整4个月!下面就随我一起看看,这个大家期待已久的版本都有哪些 ...

  6. Spring Cloud Alibaba | 序言

    目录 Spring Cloud Alibaba | 序言 1. Spring Cloud Alibaba是什么? 2. 主要功能 3. 组件 4. 版本说明 4.1 版本依赖关系 4.2 组件版本关系 ...

  7. spring cloud alibaba 简介

    ### Spring Cloud Alibaba [官方github地址](https://github.com/alibaba/spring-cloud-alibaba) Spring Cloud ...

  8. Spring Cloud Alibaba 新一代微服务解决方案

    本篇是「跟我学 Spring Cloud Alibaba」系列的第一篇, 每期文章会在公众号「架构进化论」进行首发更新,欢迎关注. 1.Spring Cloud Alibaba 是什么 Spring ...

  9. Spring Cloud Alibaba 从孵化到 "挂牌" 之旅

    背景 2014 年,Spring Boot 1.0 发布.Spring Boot 的发布绝对是 Pivotal 历史上具有里程碑意义的事件,它让我们能够非常简便地开发 Spring 应用,屏蔽了各种配 ...

  10. 厉害了,Spring Cloud Alibaba 发布 GA 版本!

    ? 小马哥 & Josh Long ? 喜欢写一首诗一般的代码,更喜欢和你共同 code review,英雄的相惜,犹如时间沉淀下来的对话,历久方弥新. 相见如故,@杭州. 4 月 18 日, ...

随机推荐

  1. ALGO基础(一)—— 排序

    ALGO基础(一)-- 排序 冒选插希快归堆,以下均为从小到大排 1 冒泡排序 描述: 比较相邻的元素.如果第一个比第二个大,就交换它们两个: 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一 ...

  2. 关于KMP算法中,获取next数组算法的理解

    参考:KMP入门级别算法详解--终于解决了(next数组详解) https://blog.csdn.net/lee18254290736/article/details/77278769 在这里讨论的 ...

  3. SQLServer 2008快速导出架构和数据脚本

    https://jingyan.baidu.com/article/454316ab715218f7a7c03a9d.html

  4. FreeBSD WIFI 配置

    ee /boot/ loader.conf ee是个编辑器 中写入 rtwn_usb_load="YES" legal.realtek.license_ack=1 在 /etc/ ...

  5. css实现0.5像素的底边框。

    由于设计图的1px在移动端开发中的像素比是2倍,在实际开发中却是需要1px的线条,虽然最直接的方式是将线条设置为0.5px,但有些移动端对于0.5px的解析为0,变成了无边框的显示.因此处理该需求我们 ...

  6. JavaCV 采集摄像头及桌面视频数据

    javacv 封装了javacpp-presets库很多native API,简化了开发,对java程序员来说比较友好. 之前使用JavaCV库都是使用ffmpeg native API开发,这种方式 ...

  7. 翻译:《实用的Python编程》06_01_Iteration_protocol

    目录 | 上一节 (5.2 封装) | 下一节 (6.2 自定义迭代) 6.1 迭代协议 本节将探究迭代的底层过程. 迭代无处不在 许多对象都支持迭代: a = 'hello' for c in a: ...

  8. mysql连接不上本地服务器或者localhost:3306报错

    今天初学MySQL数据库就遇到问题: 主要是本地服务器登录问题 workbench里双击那个connection出现的 解决方法: 1:看一看防火墙,这是最常见的,这种主要是防火墙限制了访问,可能是安 ...

  9. P1162_填涂颜色(JAVA语言)(速看!全洛谷最暴力解法!QAQ)

    思路:看了看数据n<=30,于是我们可以暴力求解(主要是BFS学的不咋地~2333).枚举每个0的位置,看上下左右四个方向上是否都有1.都有1的话说明被1包围,即在闭合圈的内部,开个数组标记一下 ...

  10. Java学习之String与int的相互转换

    •String 转 int 两种方式 int a = Integer.parseInt(s);int b = Integer.valueOf(s).intValue(); 代码 public clas ...