Seata1.5.2解决分布式事务问题

news2024/9/23 23:28:29

分布式事务–Seata

​ 前面了解到一些分布式事务的解决方案,业内也涌现出不少解决分布式事务的优秀框架,如Atomikos、Seata等,本章来了解使用下Seata。

​ Seata的前身是Fescar,而后改名Seata,简单可扩展的自治分布式事务框架。Seata为用户提供了AT、TCC、SAGA和XA事务模式(默认使用AT),致力打造的一站式分布式解决方案。

​ Seata是在传统的2PC方案上进行演进,它把一个分布式事务拆分为若干个分支事务的全局事务,全局事务协调管理若干个分支事务,使其达到一致,实现整个事务那么全部成功,要么全部失败,并且在项目中整合Seata几乎没有侵入性。

基本概念

Seata有几个基本的概念:

  • Transaction ID XID:全局唯一事务ID
  • TC(Transaction Coordinator):事务协调者,维护全局事务运行,驱动全局事务的提交和回滚
  • TM(Transaction Manager):事务管理器,定义全局事务边界,负责开启全局事务,发起全局事务的提交或回滚决议
  • RM(Resource Manager):资源管理器,管理分支事务,与TC(事务协调者)通信,决定对分支事务提交或回滚

Seata的下载和安装

Seata像Nacos一样,也有自己的服务端,需要下载服务端程序,地址是:https://github.com/seata/seata/releases

那么如何选择版本呢,还是要按照SpringCloudAlibaba的组件版本对应关系来使用.

在这里插入图片描述

笔者使用的alibaba是2021.0.4.0,seata就使用1.5.2版本即可,下载速度会挺慢,国外网址可以翻墙或者用迅雷等软件下载都可。

conf目录中,有一个application.example.yml文件,内容是关于seata的注册和配置的示例,可以修改下且改名为application.yml。

  • 可以看到配置文件中实现Seata的注册和配置方式有几种:File、Nacos、Eureka、Redis、Consul等等。我们使用Nacos即可。application.yml配置如下:
server:
  port: 7091

spring:
  application:
    name: seata-server

logging:
  config: classpath:logback-spring.xml
  file:
    path: ${user.home}/logs/seata

# 新增加的console控制台,
# 可通过访问http://localhost:7091进行登录,账号如下seata/seata
console:
  user:
    username: seata
    password: seata

seata:
  config:
    # support: nacos 、 consul 、 apollo 、 zk  、 etcd3
    type: nacos
    nacos:
      server-addr: 127.0.0.1:8848
      namespace: ded91f4b-04df-4c19-8006-755505a27c5e
      group: SEATA_GROUP
      username: nacos
      password: nacos
      # data-id: seataServer.properties
  registry:
    # support: nacos 、 eureka 、 redis 、 zk  、 consul 、 etcd3 、 sofa
    type: nacos
    nacos:
      application: seata-server
      server-addr: 127.0.0.1:8848
      group: SEATA_GROUP
      namespace: ded91f4b-04df-4c19-8006-755505a27c5e
      cluster: default
      username: nacos
      password: nacos
  # seata的安全配置
  security:
    secretKey: SeataSecretKey0c382ef121d778043159209298fd40bf3850a017
    tokenValidityInMilliseconds: 1800000
    ignore:
      urls: /,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.ico,/console-fe/public/**,/api/v1/auth/login
  • 到数据库中新增需要的数据表【Seata 1.5.2版本mysql脚本】导入压缩包目录seata/script/db/mysql.sql

在这里插入图片描述

数据表创建完毕

  • 启动nacos,创建一个dev的命名空间方便测试

在这里插入图片描述

  • 修改压缩包目录seata/script/config-center/config.txt文件中几处内容:
# 存储模式
store.mode=db
 
store.db.datasource=druid
store.db.dbType=mysql
# 需要根据mysql的版本调整driverClassName
# mysql8及以上版本对应的driver:com.mysql.cj.jdbc.Driver
# mysql8以下版本的driver:com.mysql.jdbc.Driver
store.db.driverClassName=com.mysql.jdbc.Driver
# 注意根据生产实际情况调整参数host和port
store.db.url=jdbc:mysql://127.0.0.1:3306/seata?useUnicode=true&rewriteBatchedStatements=true
# 数据库用户名密码
store.db.user=root
store.db.password=123456
# 微服务里配置与这里一致
service.vgroupMapping.dev_tx_group=default

TIPS📢:
配置事务分组service.vgroupMapping.dev_tx_group=default
dev_tx_group:需要与客户端保持一致 ,可以自定义
default:需要跟客户端和application.yml中的cluster保持一致
default 必须要等于 registry.conf cluster = “default”

  • 官方推荐通过压缩包目录seatascript/config-center/nacos/nacos-config.sh将修改后的config.txt发布到nacos上
# 运行指令 ,通过 Git Bash Here
sh nacos-config.sh -h localhost -p 8848 -g SEATA_GROUP -t ded91f4b-04df-4c19-8006-755505a27c5e

# 具体说明参见:http://seata.io/zh-cn/docs/user/configurations.html
# -h: nacos host,默认localhost
# -p: nacos端口,默认8848
# -g: nacos分组,默认'SEATA_GROUP'.
# -t: 租户信息Tenant information,对应nacos namespace ID,默认''
# -u: nacos用户名,默认''
# -w: nacos用户密码,默认''

可以看到,配置自动导入到了nacos中

在这里插入图片描述

到seata的bin目录下执行seata-server.sh或bat即可执行服务端。

客户端

那么还是按照上篇文章中的订单服务配送服务来实现。

  • 项目中导入依赖

父工程

<properties>
    <java.version>8</java.version>
    <boot.version>2.6.11</boot.version>
    <cloud.version>2021.0.4</cloud.version>
    <cloud.alibaba.version>2021.0.4.0</cloud.alibaba.version>
</properties>
<dependencyManagement>
    <dependencies>
        <!--springboot依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-dependencies</artifactId>
            <version>${boot.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <!--cloud的依赖-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>${cloud.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <!--cloud.alibaba依赖-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-alibaba-dependencies</artifactId>
            <version>${cloud.alibaba.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>
  • 子工程(订单服务和配送服务都要)
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
</dependency>
<!--seata starter -->
<dependency>
    <groupId>io.seata</groupId>
    <artifactId>seata-spring-boot-starter</artifactId>
</dependency>
  • 订单数据库配送数据库增加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;

注意📢:每个业务数据库都要有UNDO_LOG

  • 订单服务和配送服务的yml文件主要引入的配置
    • seata.enabled:是否开启自动装配seata
    • seata.application-id:应用id
    • seata.tx-service-group:事务分组
    • seata.enable-auto-datasouce-proxy:数据源自动代理
spring:
  cloud:
    nacos:
      discovery:
        group: SEATA_GROUP
        server-addr: http://localhost:8848
        # 必须填命名空间的ID
        namespace: ded91f4b-04df-4c19-8006-755505a27c5e

  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql:///order?useUnicode=true&characterEncoding=utf-8
    username: root
    password: 123456

distribution:
  name: distribution


# Seata 配置
seata:
  application-id: order-server  # 自定义
  # 是否启用数据源bean的自动代理
  enable-auto-data-source-proxy: false
  tx-service-group: dev_tx_group  # 必须和服务器配置一样
  registry:
    type: nacos
    nacos:
      # Nacos 服务地址
      server-addr: http://localhost:8848
      group: SEATA_GROUP
      namespace: ded91f4b-04df-4c19-8006-755505a27c5e
      application: seata-server # 必须和服务器配置一样
      username: nacos
      password: nacos
      cluster: default
  config:
    type: nacos
    nacos:
      server-addr: ${spring.cloud.nacos.discovery.server-addr}
      group: SEATA_GROUP
      namespace: ded91f4b-04df-4c19-8006-755505a27c5e
  service:
    vgroup-mapping:
      tx-service-group: dev_tx_group # 必须和服务器配置一样
    disable-global-transaction: false
  client:
    rm:
      # 是否上报成功状态
      report-success-enable: true
      # 重试次数
      report-retry-count: 5

feign:
  client:
    config:
      default:
        connectTimeout: 2000 # 建立连接超时时间
        readTimeout: 2000   # 读取资源超时时间
  • 配置代理数据源
@Primary
@Bean
public DataSource dataSource() {
    DruidDataSource druidDataSource = new DruidDataSource();
    druidDataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
    druidDataSource.setUrl("jdbc:mysql:///distribution?useUnicode=true&characterEncoding=utf-8");
    druidDataSource.setUsername("root");
    druidDataSource.setPassword("123456");
    DataSourceProxy dsp = new DataSourceProxy(druidDataSource);
    return dsp;
}
  • 在事务开始的地方使用注解@GlobalTransactional
@Service
public class OrderServiceImpl implements OrderService {
    @Resource
    JdbcTemplate jdbcTemplate;  // 操作数据库
    @Resource
    DistributionService distributionService;    // 远程调用

    // 模拟菜品数据
    private final Map<Integer, String> shopMap = new HashMap<Integer, String>(){{
       put(1,"菜品1");
       put(2,"菜品2");
       put(3,"菜品3");
    }};

    @Override
    @Transactional  // 加上事务
    @GlobalTransactional // seata全局事务
    public Integer createOrder(Integer id) {
        if (shopMap.containsKey(id)) {
            String orderId = UUID.randomUUID().toString().replace("-","");
            // 增加订单
            int update = jdbcTemplate.update("insert into t_order(order_id, shop_id) values(?,?)",
                    new Object[]{orderId, id});
            // 调用配送服务
            Integer result = distributionService.distribution(orderId);
            if (result <= 0) {
                throw new RuntimeException("分配配送员失败");
            }
            return update;
        }
        return null;
    }
}

踩坑

  • 前面在seata的yml中可以看到使用的是druid的连接池,mybatisPlus默认集成了druid和hikari两种连接池,而mybatis不是,因此需要在使用mybatis的项目中,另外集成druid,在application.yml中声明datasource.type为"com.alibaba.druid.pool.DruidDataSource"

  • seata服务端的配置尽量和客户端做到一致,如driver_class_name:“com.mysql.cj.jdbc.Driver”

测试

curl localhost:9002/createOrder?id=1
{"timestamp":"2023-09-16T04:59:01.614+00:00","status":500,"error":"Internal Server Error","path":"/createOrder"}

可以看到返回报错,同样去看下两个服务的日志情况:订单服务调用配送服务

在这里插入图片描述

可看到,订单服务在调用配送服务5s后,直接超时并且开始回滚数据。

配送服务日志:

在这里插入图片描述

由于本案例是一个超时情况,所以说这里报错是正常的,因为是配送服务还没执行完毕,订单服务就已经去回滚数据了,配送服务执行完毕后收到回滚的信号,也去进行回滚,发现这条xid的数据已经被订单服务回滚过了,报错没找到此XID的数据。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1014615.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

性能工程全景图、实施方案、建设目标

性能工程是一个关注系统性能层面的体系,包含测试环境的性能测试、生产环境的性能测试、性能调优、容量规划等多个方面 性能工程主要包括如下3个部分 ❑建设性能测试理论体系和流程规范、链路分析基础知识体系和流程规范、性能调优基础理论体系和流程规范。 ❑搭建高效协同的工…

js通过xpath定位元素并且操作元素以下拉框select为例

js也可以使用xpath定位元素&#xff0c;现在实例讲解。 页面上有一个下拉框&#xff0c;里面内容有三个&#xff0c;用F12看一下 一、使用xpath定位这个下拉框select eldocument.evaluate(//select[name"shoppingPreference"], document).iterateNext()二、为下拉框…

代码随想录算法训练营Day60 | 84. 柱状图中最大的矩形

文章目录 84. 柱状图中最大的矩形首尾加 0双指针 84. 柱状图中最大的矩形 题目链接 | 解题思路 本题和接雨水的题目相互呼应&#xff0c;但是难度略有提升&#xff0c;同样是一道非常棒的题&#xff01; 在接雨水中&#xff0c;需要找到每一列的左侧最大值和右侧最大值&…

高阶数据结构(2)-----红黑树

一)红黑树的基本概念和基本性质: 1)红黑树就是一种高度平衡的二叉搜索树&#xff0c;但是在每一个节点上面都增加了一个存储位来表示结点的颜色&#xff0c;可以是红色或者是黑色&#xff0c;通过对任何一条从根节点到叶子节点上面的路径各个节点着色方式的限制&#xff0c;红黑…

Selenium+Pytest自动化测试框架实战(下)

前言 本文接上篇文章哟。 一、简单学习元素定位 在日常的工作中&#xff0c;我见过很多在浏览器中直接在浏览器中右键Copy Xpath复制元素的同学。这样获得的元素表达式放在 webdriver 中去运行往往是不够稳定的&#xff0c;像前端的一些微小改动&#xff0c;都会引起元素无法…

Truenas scale 安装 Tailscale 内网穿透远程连接SMB服务

起源 没有公网IP&#xff0c;不在同一个路由器&#xff0c;没法远程连接电脑或者服务器。之前一直使用 zerotier&#xff0c; 但是这次使用 Truenas scale 安装 zerotier&#xff0c; 每次重启都变化IP&#xff0c;一直没解决。转投 tailscale。 注册 YouTube有教程&#xf…

基于ssm智能停车场031

大家好✌&#xff01;我是CZ淡陌。一名专注以理论为基础实战为主的技术博主&#xff0c;将再这里为大家分享优质的实战项目&#xff0c;本人在Java毕业设计领域有多年的经验&#xff0c;陆续会更新更多优质的Java实战项目&#xff0c;希望你能有所收获&#xff0c;少走一些弯路…

安装并设置linux虚拟机ubuntu20.04.6 LTS

1 安装虚拟机 1、安装虚拟机模拟软件virtualboxhttps://download.virtualbox.org/virtualboxhttps://download.virtualbox.org/virtualbox/7.0.10/VirtualBox-7.0.10-158379-Win.exe 2、在virtualbox中安装虚拟机ubuntu 20.04.6 LTS 桌面版https://www.releases.ubuntu.com/fo…

进度条--QProgressBar,进度对话框--QProgressDialog

一、QProgressBar 进度条 1、QProgressBar 类继承自 QWidget&#xff0c;它是一个 QWidget 部件&#xff0c; QProgressBar 除了将其放置 于进度对话框之中外&#xff0c;还可将其放置于窗口的状态栏等其他部件中。 QProgressBar提供了一个水平或垂直的进度条&#xff0c;可以…

JDBC基本概念

什么是JDBC JDBC概念 JDBC&#xff08;Java DataBase Connectivity&#xff09;是一套统一的基于Java语言的关系数据库编程接口规范。 该规范允许将SQL语句作为参数通过JDBC接口发送给远端数据库&#xff0c; …

8位和32位单片机如何选择适合,以及主要区别!

单片机直接影响到项目的成功和性能&#xff0c;我们将分享如何选择适合您的应用的8位或32位单片机。 8位单片机 vs. 32位单片机&#xff1a; 一、性能和处理能力&#xff1a; 8位单片机&#xff1a; 8位单片机通常适用于相对简单的应用&#xff0c;如传感器控制、LED显示、小…

代码随想录算法训练营第三十五天| 860.柠檬水找零 406.根据身高重建队列 452. 用最少数量的箭引爆气球

860.柠檬水找零 本题看上好像挺难&#xff0c;其实挺简单的&#xff0c;大家先尝试自己做一做。 代码随想录 public boolean lemonadeChange(int[] bills) {int five 0;int ten 0;for (int i 0; i < bills.length; i) {if (bills[i] 5) {five;} else if (bills[i] 10)…

Mysql同步数据到Doris的踩坑过程

问题背景 由于项目需要&#xff0c;需要把多个Mysql数据库的数据同步到Doris数据库&#xff0c;然后利用Doris强调的计算和查询能力&#xff0c;来满足业务需求。有关Doris可以查看它的官方文档来了解它。 seatunnel的使用到放弃 缘起 从《第十届GIAC全球互联网架构大会》了…

论如何在Android中还原设计稿中的阴影

每当设计稿上注明需要添加阴影时&#xff0c;Android上总是显得比较棘手&#xff0c;因为Android的阴影实现方式与Web和iOS有所区别。 一般来说阴影通常格式是有&#xff1a; X: 在X轴的偏移度 Y: 在Y轴偏移度 Blur: 阴影的模糊半径 Color: 阴影的颜色 何为阴影 但是在A…

手刻 Deep Learning -第壹章 -PyTorch教学-激励函数与感知机入门(上)

一、前言 本文接续前篇教学 Pytorch 与线性回归 &#xff0c;本文着重在 Activation Function &#xff08; 中文称 激励函数 &#xff09;&#xff0c;我们会介绍激励函数 &#xff08;也有人称 激活函数&#xff1f; 激发函数&#xff1f; &#xff09; 为什么会有用&#xf…

Python 06 之面向对象基础

&#x1f600;前言 在日常编程和软件开发中&#xff0c;我们通常会遇到各种各样的问题&#xff0c;其中很多问题都可以通过面向对象的程序设计方法来解决。面向对象编程不仅可以使代码更加组织化和系统化&#xff0c;而且还可以提高代码的重用性和可维护性。 . 在本教程中&…

一个工位的演讲

前几天&#xff0c;知乎官方制作了一个视频&#xff0c;感觉这个视频非常不错&#xff0c;推荐给大家看看。 这个短片很有创意&#xff0c;而且&#xff0c;内容也很丰富。 为什么说这个短片很有创意呢&#xff1f;因为它是从一个工位的角度切入的&#xff0c;所谓铁打的工位&a…

基于matlab实现的电力系统稳定性分析摆幅曲线代码

完整程序&#xff1a; clear; clc; t 0; tf 0; tfl 0.5; tc 0.5; % tc 0.05, 0.125, 0.5 sec for 2.5 cycles, 6.25 cycles & 25 cycles resp ts 0.05; m 2.52 / (180 * 50); i 2; dt 21.64 * pi / 180; ddt 0; time(1) 0; ang(1) 21.64; pm 0.9; pm1 2.44;…

解锁大数据宝藏:使用AI预测未来趋势的新方法

文章目录 大数据的价值传统方法与AI的优势使用AI预测未来趋势的新方法1. 时间序列分析2. 自然语言处理&#xff08;NLP&#xff09;3. 预测市场趋势 应用前景与挑战应用前景挑战和限制 结论 &#x1f389;欢迎来到AIGC人工智能专栏~解锁大数据宝藏&#xff1a;使用AI预测未来趋…

软件设计师考试学习1

前言 计算机组成原理及体系结构 数据的表示 进制的转换 原码反码补码移码 最高位是符号位&#xff0c;负数符号位为1 反码补码正数和原码一样&#xff0c;负数有区别 反码符号位不动&#xff0c;其他位置取反 补码在反码基础上加1 移码是将补码的符号为取反 在原码和反码中…