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

    登录 ''' 获取用户所有的数据 每条数据请求的验证 成功之后获取所有正确的信息 失败则显示错误信息 ''' #登陆页面管理 def login(request): if request.method ...

  2. 后端程序员之路 25、Redis Cluster

    官方教程和功能介绍: REDIS cluster-tutorial -- Redis中文资料站 -- Redis中国用户组(CRUG)http://www.redis.cn/topics/cluste ...

  3. SSRF攻击原理

    目录 什么是SSRF 原理 防护 什么是SSRF 一个对外的Web接口,改接口能让用户控制curl命令,去访问别的web服务. 简图如下 想象一下当用户请求的baidu.com/x.php?image ...

  4. 最新版大数据平台安装部署指南,HDP-2.6.5.0,ambari-2.6.2.0

    一.服务器环境配置 1 系统要求 名称 地址 操作系统 root密码 Master1 10.1.0.30 Centos 7.7 Root@bidsum1 Master2 10.1.0.105 Cent ...

  5. 报错问题: AtrributeError:module ‘allure’ has no attribute ‘’severity_level’

    问题:执行命令报错:pytest -s -q --alluredir report 报错问题: AtrributeError:module 'allure' has no attribute ''se ...

  6. 剑指 Offer 56 - II. 数组中数字出现的次数 II + 位运算

    剑指 Offer 56 - II. 数组中数字出现的次数 II Offer_56_2 题目详情 解题思路 java代码 package com.walegarrett.offer; /** * @Au ...

  7. day1_安装及建立数据库和表

    #第一份数据库及表create database library; use library; create table book( id int primary key, book_name char ...

  8. 如何在 ASP.NET Core 中写出更干净的 Controller

    你可以遵循一些最佳实践来写出更干净的 Controller,一般我们称这种方法写出来的 Controller 为瘦Controller,瘦 Controller 的好处在于拥有更少的代码,更加单一的职 ...

  9. 一文搞懂 this、apply、call、bind

    码文不易,转载请带上本文链接,感谢~ https://www.cnblogs.com/echoyya/p/14506269.html 目录 码文不易,转载请带上本文链接,感谢~ https://www ...

  10. vue离开页面销毁滚动事件

    methods:{ handleFun(){    /**销毁处理*/ } }, beforeDestroy(){ window.removeEventListener("scroll&qu ...