SpringCloud Alibaba之Seata分布式事务学习笔记(一)

本文涉及的产品
云数据库 RDS MySQL Serverless,0.5-2RCU 50GB
简介: SpringCloud Alibaba之Seata分布式事务学习笔记(一)

一、Seata介绍


1.1、认识Seata


SpringCloud Alibaba为我们提供了用于处理分布式事务的组件Seata。



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


实际上,就是多了一个中间人来协调所有服务的事务。


1.2、Seata的四种事务模式


Seata支持4种事务模式,官网文档:https://seata.io/zh-cn/docs/overview/what-is-seata.html


AT:本质上就是2PC的升级版,在 AT 模式下,用户只需关心自己的 “业务SQL”

一阶段,Seata 会拦截“业务 SQL”,首先解析 SQL 语义,找到“业务 SQL”要更新的业务数据,在业务数据被更新前,将其保存成“before image”,然后执行“业务 SQL”更新业务数据,在业务数据更新之后,再将其保存成“after image”,最后生成行锁。以上操作全部在一个数据库事务内完成,这样保证了一阶段操作的原子性。

二阶段如果确认提交的话,因为“业务 SQL”在一阶段已经提交至数据库, 所以 Seata 框架只需将一阶段保存的快照数据和行锁删掉,完成数据清理即可,当然如果需要回滚,那么就用“before image”还原业务数据;但在还原前要首先要校验脏写,对比“数据库当前业务数据”和 “after image”,如果两份数据完全一致就说明没有脏写,可以还原业务数据,如果不一致就说明有脏写,出现脏写就需要转人工处理。

TCC:和我们上面讲解的思路是一样的。


XA:同上,但是要求数据库本身支持这种模式才可以。


Saga:用于处理长事务,每个执行者需要实现事务的正向操作和补偿操作:


二、实战:集成Seata实现分布式事务(AT模式)


说明:demo项目其中的部分代码写的比较简陋,我们将重心放在分布式事务上即可!


对于2.1部分的代码我已经进行了打包:在对应gitee或github仓库的指定位置下载即可,可直接复现分布式事务问题!




2.1、本地项目搭建(复现分布式事务问题)


项目介绍

为了能够集成Seata组件来实现分布式事务数据一致性的效果,来构建多个微服务进行远程调用。



本次使用到的分布式组件包含:nacos(注册中心)、feign(远程调用组件)


服务包含:book-service(图书服务)、borrow-service(借阅服务)、user-service(用户服务)。


事务问题描述(目标复现):在borrow-service服务中会在一个service中会执行本地事务,远程调用book-service以及user-service的接口,这两个服务的接口都与数据库有交互操作,在没有使用Seata组件前,若是其中某个服务出现异常,那么之前提交的操作都不能够进行回滚,因为这涉及到多个不同的事务管理器。


看一下数据库表:



db_book:图书表,其中count表示该数的库存数量。对应的是book-service。



db_borrow:借阅表。对应的是borrow-service。



db_user:用户表。对应的是user-service。



Nacos服务创建命名空间

创建命名空间为seata-demo:



book-service服务



引入依赖:


<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <scope>runtime</scope>
    </dependency>
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.4.0</version>
    </dependency>
</dependencies>


1、yaml配置项:application.yaml


server:
  port: 8083
spring:
  application:
    name: book-service
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/seata-demo?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
    username: root
    password: 123456
  cloud:
    nacos:  # 如果不指定命名空间会默认注册到public里面去 如果没有指定分组 会注册到DEFAULT_GROUP
      server-addr: localhost:8848  # 指定服务注册地址
      username: nacos
      password: nacos
      discovery:
        namespace: 0245d1ab-5611-486e-8444-957bebab6d78   # 若是不指定,默认就是public
        group: BOOK_GROUP   # 若是不指定,默认是DEFAULT_GROUP
        service: book-service   # 默认使用的是spring.application.name,这里可以进行指定
#控制台打印sql(默认不会有打印sql语句)
mybatis-plus:
  mapper-locations: classpath*:/mapperxxx/**/*.xml
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl



2、Mapper接口以及Mapper配置文件、pojo对象


package com.changlu.seatauserservice.pojo;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serializable;
@Data
@EqualsAndHashCode(callSuper = false)
@TableName("db_user")
public class UserModel implements Serializable {
    private static final long serialVersionUID = 1L;
    @TableId("uid")
    private Integer uid;
    @TableField("name")
    private String name;
    @TableField("age")
    private Integer age;
    @TableField("book_count")
    private Integer bookCount;
}


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.changlu.seatabookservcie.mapper.BookMapper">
    <!-- 通用查询映射结果 -->
    <resultMap id="BaseResultMap" type="com.changlu.seatabookservcie.pojo.BookModel">
        <id column="id" property="id" />
        <result column="name" property="name" />
        <result column="count" property="count" />
    </resultMap>
</mapper>


package com.changlu.seatabookservcie.mapper;



import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.changlu.seatabookservcie.pojo.BookModel;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
/**
 * <p>
 *  Mapper 接口
 * </p>
 *
 * @author ChangLu
 * @since 2022-08-02
 */
public interface BookMapper extends BaseMapper<BookModel> {
    @Select("SELECT count from db_book WHERE id = #{id}")
    int bookRemain(Integer id);
    @Update("UPDATE db_book set count = count - 1 where id = #{id} and count > 0")
    int minusBookRemain(Integer id);
}



3、Service接口以及实现类:com.changlu.seatabookservcie.service.BookService


package com.changlu.seatabookservcie.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.changlu.seatabookservcie.pojo.BookModel;
/**
 * <p>
 *  服务类
 * </p>
 *
 * @author ChangLu
 * @since 2022-08-02
 */
public interface BookService extends IService<BookModel> {
    int bookRemain(Integer id);
    int minusBookRemain(Integer id);
}



package com.changlu.seatabookservcie.service.impl;



import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.changlu.seatabookservcie.mapper.BookMapper;
import com.changlu.seatabookservcie.pojo.BookModel;
import com.changlu.seatabookservcie.service.BookService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author ChangLu
 * @since 2022-08-02
 */
@Service
public class BookServiceImpl extends ServiceImpl<BookMapper, BookModel> implements BookService {
    @Resource
    private BookMapper bookMapper;
    @Override
    public int bookRemain(Integer id) {
        return bookMapper.bookRemain(id);
    }
    @Override
    public int minusBookRemain(Integer id) {
        return bookMapper.minusBookRemain(id);
    }
}



4、控制器,对外暴露两个接口,一个是查询以及一个更改数据:


package com.changlu.seatabookservcie.controller;
import com.changlu.seatabookservcie.service.BookService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
 * <p>
 *  前端控制器
 * </p>
 *
 * @author ChangLu
 * @since 2022-08-02
 */
@RestController
@RequestMapping("/book")
public class BookController {
    @Autowired
    private BookService bookService;
    @GetMapping("/remain/{id}")
    public int bookRemain(@PathVariable("id") Integer id) {
        return bookService.bookRemain(id);
    }
    @GetMapping("/minus/{id}")
    public int minusBookRemain(@PathVariable("id") Integer id) {
        return bookService.minusBookRemain(id);
    }
}


5、启动器开启服务注册以及Mapper扫描:启动器上添加


@MapperScan("com.changlu.seatabookservcie.mapper")
@EnableDiscoveryClient


user-service服务



引入的依赖与book-service一致,不再贴出。


1、配置文件:application.yaml


server:
  port: 8081
spring:
  application:
    name: user-service
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/seata-demo?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
    username: root
    password: 123456
  cloud:
    nacos:  # 如果不指定命名空间会默认注册到public里面去 如果没有指定分组 会注册到DEFAULT_GROUP
      server-addr: localhost:8848  # 指定服务注册地址
      username: nacos
      password: nacos
      discovery:
        namespace: 0245d1ab-5611-486e-8444-957bebab6d78   # 若是不指定,默认就是public
        group: BOOK_GROUP   # 若是不指定,默认是DEFAULT_GROUP
        service: user-service   # 默认使用的是spring.application.name,这里可以进行指定
#控制台打印sql(默认不会有打印sql语句)
mybatis-plus:
  mapper-locations: classpath*:/mapperxxx/**/*.xml
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl



2、mapper接口以及mapper映射配置文件、pojo类


package com.changlu.seataborrowservice.pojo;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serializable;
/**
 * <p>
 * 
 * </p>
 *
 * @author ChangLu
 * @since 2022-08-02
 */
@Data
@EqualsAndHashCode(callSuper = false)
@TableName("db_borrow")
public class BorrowModel implements Serializable {
    private static final long serialVersionUID = 1L;
    @TableId("user_id")
    private Integer userId;
    @TableField("book_id")
    private Integer bookId;
}



package com.changlu.seatauserservice.mapper;



import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.changlu.seatauserservice.pojo.UserModel;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
/**
 * <p>
 *  Mapper 接口
 * </p>
 *
 * @author ChangLu
 * @since 2022-08-02
 */
public interface UserMapper extends BaseMapper<UserModel> {
    @Select("SELECT book_count from db_user WHERE uid = #{uid}")
    int getUserRemainBook(Integer uid);
    @Update("UPDATE db_user set book_count = book_count - 1 where uid = #{uid}  and book_count > 0")
    int minusUserBookCount(Integer uid);
}


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.changlu.seatauserservice.mapper.UserMapper">
    <!-- 通用查询映射结果 -->
    <resultMap id="BaseResultMap" type="com.changlu.seatauserservice.pojo.UserModel">
        <id column="uid" property="uid" />
        <result column="name" property="name" />
        <result column="age" property="age" />
        <result column="book_count" property="bookCount" />
    </resultMap>
</mapper>


3、service接口:


package com.changlu.seatauserservice.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.changlu.seatauserservice.pojo.UserModel;
/**
 * <p>
 *  服务类
 * </p>
 *
 * @author ChangLu
 * @since 2022-08-02
 */
public interface UserService extends IService<UserModel> {
    int getUserRemainBook(Integer uid);
    int minusUserBookCount(Integer uid);
}



package com.changlu.seatauserservice.service.impl;


import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.changlu.seatauserservice.mapper.UserMapper;
import com.changlu.seatauserservice.pojo.UserModel;
import com.changlu.seatauserservice.service.UserService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author ChangLu
 * @since 2022-08-02
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, UserModel> implements UserService {
    @Resource
    private UserMapper userMapper;
    @Override
    public int getUserRemainBook(Integer uid) {
        return userMapper.getUserRemainBook(uid);
    }
    @Override
    public int minusUserBookCount(Integer uid) {
        return userMapper.minusUserBookCount(uid);
    }
}



4、控制器:


package com.changlu.seatauserservice.controller;
import com.changlu.seatauserservice.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
/**
 * <p>
 *  前端控制器
 * </p>
 *
 * @author ChangLu
 * @since 2022-08-02
 */
@RestController
@RequestMapping("/user")
public class UserController {
    @Autowired
    private UserService userService;
    @GetMapping("/remainbook/{uid}")
    public int getUserRemainBook(@PathVariable("uid")Integer uid) {
        return userService.getUserRemainBook(uid);
    }
    @GetMapping("/minusbook/{uid}")
    public int minusUserBookCount(@PathVariable("uid")Integer uid) {
        return userService.minusUserBookCount(uid);
    }
}


5、启动器上添加注解,与book-service一致


@MapperScan("com.changlu.seatauserservice.mapper")
@EnableDiscoveryClient

borrow-service服务(分布式事务问题产生见其中service方法)

在borrow-service服务中,还包含有feign组件,该服务会对book-service、user-service服务来进行远程调用,那么本次服务的分布式事务问题也是从这里产生的!




引入依赖:与前面服务一致同样也有nacos注册依赖,唯一多了一个就是feign组件


<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependencyManagement>
     <dependencies>
         <dependency>
             <groupId>org.springframework.cloud</groupId>
             <artifactId>spring-cloud-dependencies</artifactId>
             <version>${spring-cloud.version}</version>
             <type>pom</type>
             <scope>import</scope>
         </dependency>
     </dependencies>
</dependencyManagement>


1、配置文件:applicaion.yaml


server:
  port: 8082
spring:
  application:
    name: borrow-service
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/seata-demo?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
    username: root
    password: 123456
  cloud:
    nacos:  # 如果不指定命名空间会默认注册到public里面去 如果没有指定分组 会注册到DEFAULT_GROUP
      server-addr: localhost:8848  # 指定服务注册地址
      username: nacos
      password: nacos
      discovery:
        namespace: 0245d1ab-5611-486e-8444-957bebab6d78   # 若是不指定,默认就是public
        group: BOOK_GROUP   # 若是不指定,默认是DEFAULT_GROUP
        service: borrow-service   # 默认使用的是spring.application.name,这里可以进行指定
#控制台打印sql(默认不会有打印sql语句)
mybatis-plus:
  mapper-locations: classpath*:/mapperxxx/**/*.xml
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl


2、mapper接口以及mapper映射文件、pojo类


package com.changlu.seataborrowservice.pojo;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serializable;
/**
 * @author ChangLu
 * @since 2022-08-02
 */
@Data
@EqualsAndHashCode(callSuper = false)
@TableName("db_borrow")
public class BorrowModel implements Serializable {
    private static final long serialVersionUID = 1L;
    @TableId("user_id")
    private Integer userId;
    @TableField("book_id")
    private Integer bookId;
}


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.changlu.seataborrowservice.mapper.BorrowMapper">
    <!-- 通用查询映射结果 -->
    <resultMap id="BaseResultMap" type="com.changlu.seataborrowservice.pojo.BorrowModel">
        <id column="user_id" property="userId" />
        <result column="book_id" property="bookId" />
    </resultMap>
</mapper>


package com.changlu.seataborrowservice.mapper;



import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.changlu.seataborrowservice.pojo.BorrowModel;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
/**
 * <p>
 *  Mapper 接口
 * </p>
 *
 * @author ChangLu
 * @since 2022-08-02
 */
public interface BorrowMapper extends BaseMapper<BorrowModel> {
    @Select("select * from db_borrow where user_id = #{userId} AND book_id = #{bookId}")
    BorrowModel getBorrow(@Param("userId")Integer userId, @Param("bookId")Integer bookId);
    @Insert("insert into db_borrow(user_id, book_id) values(#{userId}, #{bookId})")
    int addBorrow(@Param("userId")Integer userId, @Param("bookId")Integer bookId);
}



3、两个服务的feign接口:


package com.changlu.seataborrowservice.feign;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
/**
 * @Description:
 * @Author: changlu
 * @Date: 7:52 PM
 */
@FeignClient(value = "user-service")
public interface BorrowUserFeign {
    @GetMapping("/user/remainbook/{uid}")
    int getUserRemainBook(@PathVariable("uid")Integer uid);
    @GetMapping("/user/minusbook/{uid}")
    int minusUserBookCount(@PathVariable("uid")Integer uid);
}


package com.changlu.seataborrowservice.feign;


import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
/**
 * @Description:
 * @Author: changlu
 * @Date: 7:52 PM
 */
@FeignClient(value = "book-service")
public interface BorrowBookFeign {
    @GetMapping("/book/minus/{id}")
    int minusBookRemain(@PathVariable("id") Integer id);
    @GetMapping("/book/remain/{id}")
    int bookRemain(@PathVariable("id") Integer id);
}



4、出现分布式事务问题的service方法:


package com.changlu.seataborrowservice.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.changlu.seataborrowservice.pojo.BorrowModel;
/**
 * @author ChangLu
 * @since 2022-08-02
 */
public interface BorrowService extends IService<BorrowModel> {
    Boolean borrow(Integer uid, Integer bookId);
}


package com.changlu.seataborrowservice.service.impl;


import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.changlu.seataborrowservice.feign.BorrowBookFeign;
import com.changlu.seataborrowservice.feign.BorrowUserFeign;
import com.changlu.seataborrowservice.mapper.BorrowMapper;
import com.changlu.seataborrowservice.pojo.BorrowModel;
import com.changlu.seataborrowservice.service.BorrowService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
/**
 * @author ChangLu
 * @since 2022-08-02
 */
@Service
public class BorrowServiceImpl extends ServiceImpl<BorrowMapper, BorrowModel> implements BorrowService {
    @Resource
    private BorrowMapper borrowMapper;
    @Autowired
    private BorrowBookFeign borrowBookFeign;
    @Autowired
    private BorrowUserFeign borrowUserFeign;
    @Override
    public Boolean borrow(Integer uid, Integer bookId) {
        //1、判断图书与用户是否都支持借阅
        if (borrowBookFeign.bookRemain(bookId) < 0) {
            throw new RuntimeException("该图书库存不足,无法借阅!");
        }
        if (borrowUserFeign.getUserRemainBook(uid) < 1){
            throw new RuntimeException("该用户借阅图书数量已上限!");
        }
        //2、扣减图书库存数量
        if (borrowBookFeign.minusBookRemain(bookId) < 1) {  //book-service服务修改数据
            throw new RuntimeException("扣减图书数量失败!");
        }
        //3、添加图书用户借阅记录
        if (borrowMapper.getBorrow(uid, bookId) != null) {
            throw new RuntimeException("用户已借阅该图书!");
        }
        if (borrowMapper.addBorrow(uid, bookId) < 1) {  //本身服务新增数据
            throw new RuntimeException("图书借阅失败!");
        }
        //4、用户自己本身借阅数量-1
        if (borrowUserFeign.minusUserBookCount(uid) < 1) {  //user-service服务修改数据
            throw new RuntimeException("用户借阅书籍数量更新有误!");
        }
        return true;
    }
}


5、控制器:


package com.changlu.seataborrowservice.controller;
import com.alibaba.fastjson.JSONObject;
import com.changlu.seataborrowservice.service.BorrowService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
 * <p>
 *  前端控制器
 * </p>
 *
 * @author ChangLu
 * @since 2022-08-02
 */
@RestController
@RequestMapping("/borrow")
public class BorrowController {
    @Autowired
    private BorrowService borrowService;
    @GetMapping("/{uid}/{bookId}")
    public JSONObject borrow(@PathVariable("uid") Integer uid, @PathVariable("bookId") Integer bookId) {
        JSONObject object = new JSONObject();
        Boolean res = false;
        try {
            res = borrowService.borrow(uid, bookId);
        }catch (Exception ex) {
            object.put("code", 500);
            object.put("msg", ex.getMessage());
            return object;
        }
        if (res) {
            object.put("code", 200);
            object.put("msg", "借阅成功!");
        }else {
            object.put("code", 500);
            object.put("msg", "借阅失败!");
        }
        return object;
    }
}



6、启动器上添加注解来进行服务注册、mapper扫描以及feign包扫描增强


@MapperScan("com.changlu.seataborrowservice.mapper")
@EnableDiscoveryClient
@EnableFeignClients


问题复现测试

提前准备


ok,此时我们的项目环境搭建已经完成,此时就来启动nacos以及我们的三个服务,来进行接口测试吧!




我们来看下数据库当前的一些数据信息:每本书的库存是3本,借阅记录当前没有,用户借阅次数是3次





开始测试


我们来访问borrow-service接口:http://localhost:8082/borrow/1/2



可以看到借阅成功!此时看一下数据库的信息:


 


可以看到西游记库存扣减1,借阅记录+1,用户借阅书籍数量-1,没有问题,那么我们此时再次调用找个接口试下:


问题提前指出:再次进行请求前我们来看下在borrow-service中的借阅方法怎么写的,若是我们再次调用上次接口,由于我们已经借阅了该书,那么此时就会在下面x位置报出异常,问题就出现了,那么book-service这里做的-1操作就产生数据不一致问题!



来吧,测试一下:果然不出所料



来看下当前的数据库吧:


 


可以看到红色横线的部分就是未回滚的book-service服务,如何解决这类问题呢?我们可以集成阿里的Seata组件来进行尝试!


相关实践学习
基于CentOS快速搭建LAMP环境
本教程介绍如何搭建LAMP环境,其中LAMP分别代表Linux、Apache、MySQL和PHP。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
1月前
|
SpringCloudAlibaba Java 持续交付
【构建一套Spring Cloud项目的大概步骤】&【Springcloud Alibaba微服务分布式架构学习资料】
【构建一套Spring Cloud项目的大概步骤】&【Springcloud Alibaba微服务分布式架构学习资料】
146 0
|
1月前
|
SpringCloudAlibaba Java 网络架构
【Springcloud Alibaba微服务分布式架构 | Spring Cloud】之学习笔记(七)Spring Cloud Gateway服务网关
【Springcloud Alibaba微服务分布式架构 | Spring Cloud】之学习笔记(七)Spring Cloud Gateway服务网关
98 0
|
23天前
|
SQL 数据库 Windows
SpringCloud集成seata分布式事务控制
SpringCloud集成seata分布式事务控制
16 0
|
1月前
|
SpringCloudAlibaba 负载均衡 Java
【Springcloud Alibaba微服务分布式架构 | Spring Cloud】之学习笔记(目录大纲)
【Springcloud Alibaba微服务分布式架构 | Spring Cloud】之学习笔记(目录大纲)
65 1
|
1月前
|
Java Nacos Sentinel
【Springcloud Alibaba微服务分布式架构 | Spring Cloud】之学习笔记(九)Nacos+Sentinel+Seata
【Springcloud Alibaba微服务分布式架构 | Spring Cloud】之学习笔记(九)Nacos+Sentinel+Seata
199 0
|
1月前
|
消息中间件 SpringCloudAlibaba Java
【Springcloud Alibaba微服务分布式架构 | Spring Cloud】之学习笔记(八)Config服务配置+bus消息总线+stream消息驱动+Sleuth链路追踪
【Springcloud Alibaba微服务分布式架构 | Spring Cloud】之学习笔记(八)Config服务配置+bus消息总线+stream消息驱动+Sleuth链路追踪
783 0
|
1月前
|
SQL 关系型数据库 数据库
学习分布式事务Seata看这一篇就够了,建议收藏
学习分布式事务Seata看这一篇就够了,建议收藏
|
2月前
|
关系型数据库 MySQL 数据库
分布式事务Seata
分布式事务Seata
|
2月前
|
Java 数据库连接 API
分布式事物【XA强一致性分布式事务实战、Seata提供XA模式实现分布式事务】(五)-全面详解(学习总结---从入门到深化)
分布式事物【XA强一致性分布式事务实战、Seata提供XA模式实现分布式事务】(五)-全面详解(学习总结---从入门到深化)
57 0
|
开发框架 Java 数据库连接
分布式事物【XA强一致性分布式事务实战、Seata提供XA模式实现分布式事务】(五)-全面详解(学习总结---从入门到深化)(下)
分布式事物【XA强一致性分布式事务实战、Seata提供XA模式实现分布式事务】(五)-全面详解(学习总结---从入门到深化)
37 0

热门文章

最新文章