Seata(分布式事务集成测试和总结)

文章目录

1.集成测试

1.集成测试正常下单
1.步骤
2.浏览器访问 http://localhost:10008/order/save?userId=666\&productId=1\&nums=1\&money=100
3.注意事项和细节
2.集成测试模拟异常
1.步骤
1.com/sun/springcloud/controller/StorageController.java 休眠12s,模拟openfeign超时
2.仍然按照正常步骤启动并测试
3.预测结果
  • 保存订单,扣减账户余额,扣减库存成功!但是修改订单状态失败,也就是status是0!
2.执行前数据库状态
order表
account表
storage表
3.浏览器输入 http://localhost:10008/order/save?userId=666\&productId=1\&nums=2\&money=200,12s后查看数据库状态
order表
account表(这里在写sql时并没有计算数量,所以确实应该减去200)
storage表(库存减2)
3.集成测试分布式事务控制
1.在com/sun/springcloud/service/Impl/OrderServiceImpl.java 的save方法加上@GlobalTransactional进行分布式事务控制
2.测试步骤
3.全部启动后查看nacos注册情况
4.测试前数据库状态
5.浏览器请求 http://localhost:10008/order/save?userId=666\&productId=1\&nums=1\&money=100
5.注意事项

2.seata分布式事务总结

1.工作机制
2.seata的使用流程
1.安装
2.修改 D:\seata\conf\file.conf文件
1.修改事务组(需要进行分布式事务控制的微服务在同一组)
2.修改日志存储模式为db
3.修改数据库(MySQL5.7)连接信息
3.修改registry.conf 配置注册中心nacos(seata需要注册到nacos)
4.创建seata数据库和表
1.创建seata数据库
sql 复制代码
# 创建数据库 seata
create database seata;
use seata;
2.复制db_store.sql的内容,创建需要的表
5.启动nacos和seata进行测试,seata会注册到nacos
6.为每个需要分布式事务控制的数据库执行 db_undo_log.sql,创建undo_log表,用于事务回滚
7.pom.xml引入依赖
  • 注意这里引入的nacos是nacos-discovery starter,没有整合Seata之前引入的跟这个不一样!!!
xml 复制代码
    <dependencies>
        <!-- 提示 application.yml -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>

        <!-- 引入 openfeign -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

        <!-- 在微服务模块引入 nacos-discovery starter -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>

        <!--seata-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
            <exclusions>
                <!-- 排除自带的 seata-all -->
                <exclusion>
                    <artifactId>seata-all</artifactId>
                    <groupId>io.seata</groupId>
                </exclusion>
            </exclusions>
        </dependency>
        <!-- 引入指定版本的 io.seata -->
        <dependency>
            <groupId>io.seata</groupId>
            <artifactId>seata-all</artifactId>
            <version>0.9.0</version>
        </dependency>

        <!-- springboot web starter -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <!-- 如果在子工程/模块指定了 version,则以指定为准 -->
        </dependency>

        <!--
        1. starter-actuator 是 springboot 程序的监控系统,可以实现健康检查,info 信息
        等
        2. 访问 http://localhost:10000/actuator 可以看到相关链接, 还可以做相关设置. -->
        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web
        -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

        <!-- mybatis -->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
        </dependency>

        <!-- druid -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <!-- 这里我们重新指定一下 version 因为父项目中没有对这个依赖进行版本仲裁-->
            <version>1.1.13</version>
        </dependency>

        <!-- mysql -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

        <!-- jdbc -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>

        <!-- lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <!-- test -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!-- 公共模块的jar包 -->
        <dependency>
            <groupId>org.example</groupId>
            <artifactId>e_commerce_center-common-api</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>
8.application.yml
yaml 复制代码
server:
  port: 10010 # 配置服务端口
spring:
  application:
    name: seata-storage-micor-service # 配置服务的名称,名字任意这里与项目名保持一致
  cloud:
    alibaba:
      seata:  # 配置seata
        tx-service-group: sun_order_tx_group # 自定义事务组名称,与\conf\file.conf保持一致
    nacos: # 配置nacos
      discovery:
        server-addr: localhost:8848
  datasource: # 配置数据源
    driver-class-name: com.mysql.jdbc.Driver
    # 别忘记创建数据库之后修改数据库名称
    url: 
    username: 
    password: 
logging:  # 配置seata日志级别
  level:
    io:
      seata: info
mybatis:
  mapperLocations: classpath:mapper/*.xml # 扫描所有Mapper.xml
  configuration:
    map-underscore-to-camel-case: true # 开启驼峰命名
9.D:\seata\conf下的两个配置文件复制到src/main/resources下并修改
1.将file.conf复制到src/main/resources下,然后修改这行,seata的服务ip+端口以及db根据实际情况修改(前面是修改过的)
2.registry.conf复制到src/main/resources下,然后配置注册中心nacos(前面也配过,直接复制即可)
3.最终的文件目录
10.进行业务逻辑编写
1.创建实体类
  • 如果数据库中的数据是sun_name则在实体类中可以编写为sunName,不过这样需要在application.yml中开启自动驼峰命名(上面配置了)
  • 这个就相当于在每个Mapper.xml中加了一个resultMap映射,如果实体类属性和表的字段可以被自动驼峰命名所映射那么就不需要再写resultMap
2.dao层
  • Mapper接口注入容器
  • Mapper接口的@Param注解一旦指定,就不需要在xml实现的时候指定参数类型了,直接使用#{}来取出数据即可
  • Mapper.xml在application.yml中已经配置了自动扫描
3.service层
  • service接口编写
  • service实现类注入容器
4.controller层
  • 注入容器
  • 在微服务中如果使用GetMapping则参数需要添加@RequestParam
  • 如果使用PostMapping则参数需要添加@RequestBody
11.两个常规配置类
1.MyBatisConfig.java 配置类,依赖注入所有Mapper接口(不用加@Mapper注解了)
java 复制代码
package com.sun.springcloud.config;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Configuration;

/**
 * Description: MyBatis配置类,扫描所有Mapper接口,这样就不用在每个Mapper接口上添加@Mapper注解
 *
 * @Author sun
 * @Create 2024/3/31 13:16
 * @Version 1.0
 */
@MapperScan("com.sun.springcloud.dao")
@Configuration
public class MyBatisConfig {
}
2.DataSourceProxyConfig.java 配置数据源代理为 seata
  • 这里需要注意:配置文件中的mybatis.mapperLocations是以驼峰命名的
java 复制代码
package com.sun.springcloud.config;

import com.alibaba.druid.pool.DruidDataSource;
import io.seata.rm.datasource.DataSourceProxy;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.transaction.SpringManagedTransactionFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;

import javax.sql.DataSource;

/**
 * Description: 配置数据源的代理是 seata
 *
 * @Author sun
 * @Create 2024/3/31 13:28
 * @Version 1.0
 */
@Configuration
public class DataSourceProxyConfig {
    // 配置文件中的mybatis.mapperLocations
    @Value("${mybatis.mapperLocations}")
    private String mapperLocations; // 1.mybatis的mapper文件位置

    // 配置数据源
    @Bean
    @ConfigurationProperties(prefix = "spring.datasource") // 2.读取配置文件中的数据源配置
    public DataSource druidDataSource() {
        return new DruidDataSource();
    }

    // 配置数据源代理为seata的DataSourceProxy
    @Bean // 配置数据源代理引入的包: io.seata.rm.datasource.DataSourceProxy
    public DataSourceProxy dataSourceProxy(DataSource dataSource) {
        return new DataSourceProxy(dataSource);
    }

    // 配置SqlSessionFactory为seata的SqlSessionFactoryBean
    @Bean // 配置SqlSessionFactory,常规配置
    public SqlSessionFactory sqlSessionFactoryBean(DataSourceProxy dataSourceProxy)
            throws Exception {
        SqlSessionFactoryBean sqlSessionFactoryBean =
                new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSourceProxy);
        sqlSessionFactoryBean.setMapperLocations
                (new PathMatchingResourcePatternResolver().getResources(mapperLocations)); // 3.mybatis的mapper文件位置
        sqlSessionFactoryBean.setTransactionFactory
                (new SpringManagedTransactionFactory());
        return sqlSessionFactoryBean.getObject();
    }
}
12.创建主启动类(示例)
java 复制代码
package com.sun.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;

/**
 * Description:
 *
 * @Author sun
 * @Create 2024/3/31 13:37
 * @Version 1.0
 */
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class}) // 取消数据源的自动创建,使用代理数据源
@EnableDiscoveryClient // 开启服务发现
@EnableFeignClients // 开启Feign客户端
public class SeataStorageMicroServiceApplication10010 {
    public static void main(String[] args) {
        SpringApplication.run(SeataStorageMicroServiceApplication10010.class, args);
    }
}
13.OpenFeign远程调用细节
1.在前面的依赖中已经引入了OpenFeign,并且在主启动类中也开启了Feign客户端
2.service接口声明需要远程调用的controller
  • 这里的FeignClient可以进行服务发现,得到要调用的服务的ip+端口+上下文路径
  • 这里的RequestMapping可以找到远程调用的服务的资源路径,与服务发现的路径进行拼接即可找到指定资源
  • 具体声明的方式就是
    1. 添加@FeignClient注解,指定要远程调用服务的application-name(注意不能带_)
    2. 将需要调用的controller直接复制过来,然后去掉方法体即可
java 复制代码
package com.sun.springcloud.service;

import com.sun.springcloud.util.Result;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestMapping;

/**
 * Description:
 *
 * @Author sun
 * @Create 2024/3/31 16:19
 * @Version 1.0
 */
@FeignClient(value = "seata-storage-micor-service")
public interface StorageService {
    @RequestMapping("/storage/reduce")
    public Result reduce(Long productId, Integer nums);
}
3.远程调用的方式
  • 通过依赖注入针对service接口的代理对象进行远程调用
4.谁可以远程调用
  • service接口实现类
  • controller
相关推荐
武子康24 分钟前
Java-72 深入浅出 RPC Dubbo 上手 生产者模块详解
java·spring boot·分布式·后端·rpc·dubbo·nio
橘子在努力4 小时前
【橘子分布式】Thrift RPC(理论篇)
分布式·网络协议·rpc
lifallen6 小时前
Kafka 时间轮深度解析:如何O(1)处理定时任务
java·数据结构·分布式·后端·算法·kafka
沈健_算法小生8 小时前
基于SpringBoot3集成Kafka集群
分布式·kafka·linq
Swift社区9 小时前
ELK、Loki、Kafka 三种日志告警联动方案全解析(附实战 Demo)
分布式·elk·kafka
chanalbert17 小时前
Nacos 技术研究文档(基于 Nacos 3)
spring boot·分布式·spring cloud
线条119 小时前
Spark 单机模式安装与测试全攻略
大数据·分布式·spark
C182981825751 天前
分布式ID 与自增区别
分布式
码字的字节1 天前
深入解析Hadoop架构设计:原理、组件与应用
大数据·hadoop·分布式·hadoop架构设计
悟能不能悟1 天前
Dubbo跨越分布式事务的最终一致性陷阱
分布式·wpf·dubbo