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

    C++核心编程 本阶段主要针对C++面向对象编程技术做详细讲解,探讨C++中的核心和精髓. 1 内存分区模型 C++程序在执行时,将内存大方向划分为4个区域 代码区:存放函数体的二进制代码,由操作系统 ...

  2. Go中定时器实现原理及源码解析

    转载请声明出处哦~,本篇文章发布于luozhiyun的博客:https://www.luozhiyun.com 本文使用的go的源码15.7,需要注意的是由于timer是1.14版本进行改版,但是1. ...

  3. 基于dlib+django+python 实现web端人脸打卡

    face_recognition 基于python+django+dlib实现的人脸打卡系统 开始之前 windows用户需要安装 VS2017 其他VS版本也行 linux用户需要安装c++编译器( ...

  4. CVE-2014-4210 SSRF漏洞

    Weblogic中存在一个SSRF漏洞,利用该漏洞可以发送任意HTTP请求,进而攻击内网中redis.fastcgi等脆弱组件. 修复方式: 1.删除server/lib/uddiexplorer.w ...

  5. 进阶宝典一|SqlServer数据库自动备份设置

    很多人都没机会接触到数据库备份,经常操作的要么是数据库管理员,要么是项目负责人.那是不是说数据库备份就不用学了? 不,其实作为开发人员应该要了解数据备份,数据备份的手段有很多:软件备份.脚本备份.其他 ...

  6. 翻译:《实用的Python编程》05_01_Dicts_revisited

    目录 | 上一节 (4.4 异常) | 下一节 (5.2 封装) 5.1 再谈字典 Python 对象系统主要基于字典实现.本节将对此进行讨论. 字典 字典是命名值(named values)的集合. ...

  7. 小技巧!CSS 整块文本溢出省略特性探究

    今天的文章很有意思,讲一讲整块文本溢出省略打点的一些有意思的细节. 文本超长打点 我们都知道,到今天(2020/03/06),CSS 提供了两种方式便于我们进行文本超长的打点省略. 对于单行文本,使用 ...

  8. Spring Boot 启动过程

    一切从SpringApplication.run()开始,最终返回一个ConfigurableApplicationContext 构造了一个SpringApplication对象,然后调用它的run ...

  9. java实现所有排序算法

    package sort;public class Sort { public static void BubbleSort(int[] arr) { //TODO 冒泡排序 for(int i=ar ...

  10. socket 之send和recv原理剖析

    认识TCP socket的发送缓冲区和接收缓冲区 当创建一个TCP socket对象的时候会有一个发送缓冲区和一个接收缓冲区,相当与内存中的一片空间 send原理剖析 send是不是直接把数据发送给服 ...