SpringCloud alibaba Seata安装与使用

Seata是什么?

Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案。

Seata安装

  • 下载地址:seata下载
    SpringCloud alibaba Seata安装与使用_第1张图片
  • 下载成功后解压文件并修改配置文件
    • file.conf
      自定义事务组名称SpringCloud alibaba Seata安装与使用_第2张图片
      事务日志存储模式改为db
      SpringCloud alibaba Seata安装与使用_第3张图片
    • registry.conf
      指明注册中心为nacosSpringCloud alibaba Seata安装与使用_第4张图片
  • mysql8.0以上的需要替换lib文件下的mysql的jar包
    SpringCloud alibaba Seata安装与使用_第5张图片
  • 数据库执行db_store.sql脚本(seata1.0版本之前的conf配置文件下有)
    SpringCloud alibaba Seata安装与使用_第6张图片
  • 先启动nacos;然后双击bin目录下的seata-server.bat文件启动seata。可看到seata成功启动并注册到nacos中。
    SpringCloud alibaba Seata安装与使用_第7张图片

seata使用(加@GlobalTransactional注解)

分布式环境准备

数据库创建

  • seata_order库下新建订单表t_order
    SpringCloud alibaba Seata安装与使用_第8张图片
  • seata_storage库下新建库存表t_storage
    SpringCloud alibaba Seata安装与使用_第9张图片
  • seata_account库下新建账户表t_account
    SpringCloud alibaba Seata安装与使用_第10张图片
  • 三个表下建立对应的回滚日志表,执行db_undo_log.sql脚本(seata1.0版本之前的conf配置文件下有)
    SpringCloud alibaba Seata安装与使用_第11张图片

微服务代码实现

新建seata-order-8001seata-storage-8002seata-account-8003Module
下面以seata-order-8001为例

  1. 新建seata-order-8001Module
  2. POM文件
<!-- nacos -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- nacos -->

<!-- seata-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
    <exclusions>
        <exclusion>
            <groupId>io.seata</groupId>
            <artifactId>seata-all</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>io.seata</groupId>
    <artifactId>seata-all</artifactId>
    <version>1.0.0</version>
</dependency>
<!-- seata-->
<!--feign-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!--feign-->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.16.18</version>
    <optional>true</optional>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>1.3.0</version>
</dependency>

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.1.10</version>
</dependency>
  1. yaml文件
server:
  port: 8001
spring:
  application:
    name: seata-order
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848    #nacos地址
    alibaba:
      seata:
        tx-service-group: st_tx_group  #seata事务组名称,对应file.conf文件
  #数据库配置
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/seata_order?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Hongkong&useSSL=false
    username: root
    password: root
feign:
  hystrix:
    enabled: false
logging:
  level:
    io:
      seata: info
mybatis:
  mapper-locations: classpath:/mapper/*.xml
  1. 拷贝seata-server/conf目录下的file.conf到resources目录下
    SpringCloud alibaba Seata安装与使用_第12张图片
  2. 拷贝seata-server/conf目录下的registry.conf到resources目录下
  3. 新建实体类文件Order.java和统一结果返回文件CommonResult.java

Order.java

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Order {

    private Long id;

    private Long userId;

    private Long productId;

    private Integer count;

    private BigDecimal money;

    private Integer status;

}

CommonResult.java


@Data
@AllArgsConstructor
@NoArgsConstructor
public class CommonResult<T> {

    private Integer code;

    private String message;

    private T      date;

    public CommonResult(Integer code, String message){
        this(code,message,null);
    }

}
  1. dao接口以及mapper文件

OrderDao.java

@Mapper
public interface OrderDao {

    void creat(Order order); //创建订单

    void update(@Param("userId") Long usrId,@Param("status") Integer status); //修改订单状态
}

OrderMapper.xml



<mapper namespace="com.cloud.dao.OrderDao">
    <resultMap id="BaseResultMap" type="com.cloud.dao.OrderDao">
        <id column="id" property="id" jdbcType="BIGINT">id>
        <result column="user_id" property="userId" jdbcType="BIGINT">result>
        <result column="product_id" property="productId" jdbcType="BIGINT">result>
        <result column="count" property="count" jdbcType="INTEGER">result>
        <result column="status" property="status" jdbcType="INTEGER">result>
    resultMap>
    <insert id="creat">
        insert into t_order(id,user_id,product_id,count,money,status) values
        (null,#{userId},#{productId},#{count},#{money},0);
    insert>
    <update id="update">
        update t_order set status=1
        where user_id=#{userId} and status=#{status}
    update>

mapper>
  1. service接口以及实现

OrderService.java

public interface OrderService {
    void creat(Order order);

}

AccountService.java

@Component
@FeignClient(value = "seata-account")
public interface AccountService {

    @PostMapping(value = "/account/decrease")
    CommonResult decrease(@RequestParam("userId") Long userId, @RequestParam("money") BigDecimal money);

}

StorageService.java

@Component
@FeignClient(value = "seata-storage")
public interface StorageService {

    @PostMapping(value = "/storage/decrease")
    CommonResult decrease(@RequestParam("productId") Long productId,@RequestParam("count") Integer count);

}

OrderServiceImpl.java

@Service
@Slf4j
public class OrderServiceImpl implements OrderService {
    @Resource
    private OrderDao orderDao;

    @Resource
    private AccountService accountService;

    @Resource
    private StorageService storageService;
    
    @Override
    public void creat(Order order) {
        log.info("-->创建订单");
        orderDao.creat(order);

        log.info("-->减少库存");
        storageService.decrease(order.getProductId(),order.getCount());

        log.info("-->减少账号余额");
        accountService.decrease(order.getUserId(),order.getMoney());

        log.info("-->修改订单状态");
        orderDao.update(order.getUserId(),0);

        log.info("-->结束");

    }
  1. controller
@RestController
public class OrderController {
    @Resource
    private OrderService orderService;

    @GetMapping("/order/create")
    public CommonResult create(Order order){
        orderService.creat(order);
        return new CommonResult(200,"创建成功");
    }
}
  1. config配置

MyBatisConfig.java

@Configuration
@MapperScan({"com.cloud.dao"})
public class MyBatisConfig {
}

DataSourceProxyConfig.java

//使用seata对数据源代理
@Configuration
public class DataSourceProxyConfig {
    @Value("${mybatis.mapper-locations}")
    private String mapperLocations;

    @Bean
    @ConfigurationProperties(prefix = "spring.datasource")
    public DataSource druidDataSource(){
        return new DruidDataSource();
    }

    @Bean
    public DataSourceProxy dataSourceProxy(DataSource dataSource){
        return new DataSourceProxy(dataSource);
    }

    @Bean
    public SqlSessionFactory sqlSessionFactoryBean(DataSourceProxy dataSourceProxy) throws Exception{
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSourceProxy);
        sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(mapperLocations));
        sqlSessionFactoryBean.setTransactionFactory(new SpringManagedTransactionFactory());
        return sqlSessionFactoryBean.getObject();
    }
}
  1. 主启动
@EnableDiscoveryClient
@EnableFeignClients
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)//取消数据源的自动创建
public class SeataOrderMain {
    public static void main(String[] args) {
        SpringApplication.run(SeataOrderMain.class,args);
    }
}

不加@GlobalTransactional注解

  • 库存服务做超时处理(openFeign默认等待1秒,超时则报错)
@RequestMapping("/storage/decrease")
    public CommonResult decrease(@RequestParam("productId") Long productId, @RequestParam("count") Integer count) throws InterruptedException {
        TimeUnit.SECONDS.sleep(3); //等待3秒
        storageService.decrease(productId,count);
        return new CommonResult(200,"库存减少成功");
    }
  • 结果
    创建订单报错
    SpringCloud alibaba Seata安装与使用_第13张图片
    订单表t_order订单创建成功
    SpringCloud alibaba Seata安装与使用_第14张图片
    库存表t_storage库存减少成功
    SpringCloud alibaba Seata安装与使用_第15张图片
    账户表t_account账户没有发生改变
    SpringCloud alibaba Seata安装与使用_第16张图片

加上@GlobalTransactional注解

OrderServiceImpl.java的creat方法上加上@GlobalTransactional注解,同样库存服务做超时处理,数据库数据恢复初始设置。

@Override
@GlobalTransactional
public void creat(Order order) {
    log.info("-->创建订单");
    orderDao.creat(order);

    log.info("-->减少库存");
    storageService.decrease(order.getProductId(),order.getCount());

    log.info("-->减少账号余额");
    accountService.decrease(order.getUserId(),order.getMoney());

    log.info("-->修改订单状态");
    orderDao.update(order.getUserId(),0);

    log.info("-->结束");

}

创建订单报错
SpringCloud alibaba Seata安装与使用_第17张图片
数据库此时三张表并未发生改变
订单表t_order未发生改变
SpringCloud alibaba Seata安装与使用_第18张图片
库存表t_storage未发生改变
SpringCloud alibaba Seata安装与使用_第19张图片
账户表t_account未发生改变
SpringCloud alibaba Seata安装与使用_第20张图片

你可能感兴趣的:(springcloud)