seata1.4的使用,以及解决seata启动闪退问题,seata原理

sprngcoud alibaba seata1.4集群使用(个人笔记)

1.下载

官网seata.io

2.建库建表

create database seata;
-- -------------------------------- The script used when storeMode is 'db' -------------------------------- -- the table to store GlobalSession data CREATE TABLE IF NOT EXISTSglobal_table(xidVARCHAR(128) NOT NULL,transaction_idBIGINT,statusTINYINT NOT NULL,application_idVARCHAR(32),transaction_service_groupVARCHAR(32),transaction_nameVARCHAR(128),timeoutINT,begin_timeBIGINT,application_dataVARCHAR(2000),gmt_createDATETIME,gmt_modifiedDATETIME, PRIMARY KEY (xid), KEYidx_gmt_modified_status(gmt_modified,status), KEYidx_transaction_id(transaction_id`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8;

– the table to store BranchSession data
CREATE TABLE IF NOT EXISTS branch_table
(
branch_id BIGINT NOT NULL,
xid VARCHAR(128) NOT NULL,
transaction_id BIGINT,
resource_group_id VARCHAR(32),
resource_id VARCHAR(256),
branch_type VARCHAR(8),
status TINYINT,
client_id VARCHAR(64),
application_data VARCHAR(2000),
gmt_create DATETIME(6),
gmt_modified DATETIME(6),
PRIMARY KEY (branch_id),
KEY idx_xid (xid)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8;

– the table to store lock data
CREATE TABLE IF NOT EXISTS lock_table
(
row_key VARCHAR(128) NOT NULL,
xid VARCHAR(96),
transaction_id BIGINT,
branch_id BIGINT NOT NULL,
resource_id VARCHAR(256),
table_name VARCHAR(32),
pk VARCHAR(36),
gmt_create DATETIME,
gmt_modified DATETIME,
PRIMARY KEY (row_key),
KEY idx_branch_id (branch_id)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8;`

3.修改配置文件

conf目录下file.conf文件
在这里插入图片描述

在这里插入图片描述
以上所填的数据库地址就是,刚才建表建库的那个地址。(上面的那些表都是在全局事务运行期间所需要的,但是事务一结束就会自动删除掉里面的数据)

4.修改registry.conf文件

在这里插入图片描述
在这里插入图片描述

5.一些电脑没有配置的好的话是无法启动的

用cmd命令窗口启动可以看到错误信息。

错误可能1
他可能会提示一个找不到 jvm.dll文件 你可以在他给的那个目录下的某一个文件夹下去找这个文件(直接在那个目录下进行搜索)
错误可能2
内存不足
可以直接修改seata-server.bat这个文件中的内容
在这里插入图片描述
此时便可完美启动。(前提必须启动nacos服务器)

启动成功后可在nacos中看到

在这里插入图片描述

6.版本控制

1.父项目pom

 <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.target>1.8</maven.compiler.target>
        <maven.compiler.source>1.8</maven.compiler.source>
        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>
        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>
        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>
    </properties>

    <!--
        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件,进行依赖版本的管理,防止不兼容。
        在 https://dwz.cn/mcLIfNKt 文章中,Spring Cloud Alibaba 开发团队推荐了三者的依赖关系
     -->
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-parent</artifactId>
                <version>${spring.boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring.cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>${spring.cloud.alibaba.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

2.子项目pom

<dependencies>
        <!-- 实现对 Spring MVC 的自动化配置 -->
        <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>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency> <!-- 本示例,我们使用 MySQL -->
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.48</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.10</version>
        </dependency>

        <!-- 实现对 MyBatis 的自动化配置 -->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.2</version>
        </dependency>

        <!-- 引入 Spring Cloud Alibaba Seata 相关依赖,使用 Seata 实现分布式事务,并实现对其的自动配置 -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-alibaba-seata</artifactId>
        </dependency>
        <dependency> <!-- 主要想使用 seata 1.1.0 版本 -->
            <groupId>io.seata</groupId>
            <artifactId>seata-spring-boot-starter</artifactId>
            <version>1.4.0</version>
        </dependency>

        <!-- 引入 Spring Cloud Alibaba Nacos Discovery 相关依赖,将 Nacos 作为注册中心,并实现对其的自动配置 -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>

        <!-- 引入 Spring Cloud OpenFeign 相关依赖,使用 OpenFeign 提供声明式调用,并实现对其的自动配置 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.16</version>
        </dependency>
    </dependencies>

7.账户服务

1.mapper

@Mapper
public interface AccountMapper {
    public void payment(@Param("userId") Long userId, @Param("money") BigDecimal money);
}

2.service

public interface AccountService {
    public CommonResult payment(Long userId, BigDecimal money);
}

3.serviceimpl

@Service
@Slf4j
public class AccountServiceImpl implements AccountService {

    @Resource
    private AccountMapper accountMapper;

    @Override
    public CommonResult payment(Long userId, BigDecimal money) {
        log.info("开始支付");
        int a = 1/0;
        accountMapper.payment(userId, money);
        log.info("支付成功");
        return new CommonResult(200,"支付成功");
    }
}

4.controller

@RestController
public class AccountController {

    @Autowired
    private AccountService accountService;

    @PostMapping("/account/payment")
    public CommonResult payment(@RequestParam("userId")Long userId, @RequestParam("money") BigDecimal money){
       return  accountService.payment(userId,money);
    }
}

5.domain

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Account {
 
    private Long id;
 
    /**
     * 用户id
     */
    private Long userId;
 
    /**
     * 总额度
     */
    private BigDecimal total;
 
    /**
     * 已用额度
     */
    private BigDecimal used;
 
    /**
     * 剩余额度
     */
    private BigDecimal residue;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class CommonResult<T>
{
    private Integer code;
    private String  message;
    private T       data;
 
    public CommonResult(Integer code, String message)
    {
        this(code,message,null);
    }
}

6.mapper映射文件

<?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.ljc.springcloud.mapper.AccountMapper">

    <resultMap id="BaseResultMap" type="com.ljc.springcloud.domain.Account">
        <id column="id" property="id" jdbcType="BIGINT"/>
        <result column="user_id" property="userId" jdbcType="BIGINT"/>
        <result column="total" property="total" jdbcType="DECIMAL"/>
        <result column="used" property="used" jdbcType="DECIMAL"/>
        <result column="residue" property="residue" jdbcType="DECIMAL"/>
    </resultMap>

    <update id="payment">
        UPDATE t_account
        SET
          residue = residue - #{money},used = used + #{money}
        WHERE
          user_id = #{userId};
    </update>

</mapper>

7.主启动

@SpringBootApplication//开启自动配置
@EnableFeignClients//表示是feign客户端(本次demo可以不要,因为它没有远程调用)
@EnableDiscoveryClient//注册到服务注册中心
@MapperScan("com.ljc.springcloud.mapper")//mybatis扫描mapper包
public class AccountMain2003 {
    public static void main(String[] args) {
        SpringApplication.run(AccountMain2003.class,args);
    }
}

8.yaml

server:
  port: 2003

spring:
  application:
    name: seata-account-service
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://192.168.83.130:3306/seata_account?useSSL=false&useUnicode=true&characterEncoding=UTF-8
    username: root
    password: root

seata:
  application-id: ${spring.application.name} # Seata 应用编号,默认为 ${spring.application.name}
  tx-service-group: ${spring.application.name}-group # Seata 事务组编号,用于 TC 集群名
  # Seata 服务配置项,对应 ServiceProperties 类
  service:
    # 虚拟组和分组的映射
    vgroup-mapping:
      seata-account-service-group: default
  # Seata 注册中心配置项,对应 RegistryProperties 类
  registry:
    type: nacos # 注册中心类型,默认为 file
    nacos:
      cluster: default # 使用的 Seata 分组
      namespace: # Nacos 命名空间
      serverAddr: localhost # Nacos 服务地址

logging:
  level:
    io:
      seata: info

mybatis:
  mapperLocations: classpath:mapper/*.xml

8.库存服务

1.mapper

@Mapper
public interface StorageMapper {
    public void update(@Param("productId") Long productId, @Param("count") Integer count);
}

2.service

public interface StorageService {
    public void update(Long productId,Integer count);
}

3.serviceImpl

@Service
@Slf4j
public class StorageServiceImpl implements StorageService {

    @Resource
    private StorageDao storageDao;


    @Override
    public void update(Long productId, Integer count) {
        log.info("StorageServiceImpl  减库存");
        storageDao.update(productId,count);
        log.info("StorageServiceImpl  减库存成功");
    }

}

4.controller

@RestController
public class StorageController {

    @Autowired
    private StorageService storageService;


    @PostMapping("/storage/update")
    public CommonResult update(@RequestParam("productId") Long productId, @RequestParam("count") Integer count){
        System.out.println(productId+"             "+count);
        storageService.update(productId,count);
        return new CommonResult(200,"出库成功!!!");
    }
}

5.domain

@Data
public class Storage {
 000
    private Long id;
 
    // 产品id
    private Long productId;
 
    //总库存
    private Integer total;
 
 
    //已用库存
    private Integer used;
 
  
    //剩余库存
    private Integer residue;
}

6.yaml与以上相同(只是端口和服务名不同)
7.xml映射文件

<?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.ljc.springcloud.dao.StorageDao">

    <resultMap id="storageMap" type="com.ljc.springcloud.domain.Storage">
        <id property="id" column="id" jdbcType="BIGINT"/>
        <result property="productId" column="productId" jdbcType="BIGINT"/>
        <result property="total" column="total" jdbcType="INTEGER"/>
        <result property="used" column="used" jdbcType="BIGINT"/>
        <result property="residue" column="residue" jdbcType="BIGINT"/>
    </resultMap>


    <update id="update">
        update t_storage set used = used + #{count},residue = residue - #{count} where product_id = #{productId};
    </update>
</mapper>

8.主启动

@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
@MapperScan({"com.ljc.springcloud.mapper"})
public class StorageMain2002 {
    public static void main(String[] args) {
        SpringApplication.run(StorageMain2002.class,args);
    }
}

9.订单服务

1.mappr

@Mapper
public interface OrderMapper {

    /**
     * 订单业务
     * @param order
     */
    public void createOrder(Order order);

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

2.service

public interface OrderService {
    /**
     * 创建订单
     * @param order
     */
    public void createOrder(Order order);

}

3.serviceImpl

@Service
@Slf4j
public class OrderServiceImpl implements OrderService {
    @Resource
    private AccountService accountService;
    @Resource
    private OrderDao orderDao;
    @Resource
    private StorageService storageService;
    @Override
    public void createOrder(Order order) {
        log.info("订单创建...........");
        orderDao.createOrder(order);
        log.info("订单创建成功.......");
        log.info("减库存.............");
        storageService.update(order.getProductId(),order.getCount());
        log.info("减库存成功.........");
        log.info("账户扣款...........");
        accountService.payment(order.getUserId(),order.getMoney());
        log.info("账户扣款成功.......");
        log.info("修改订单状态.......");
        orderDao.update(order.getUserId(),0);
        log.info("修改订单状态成功...");
    }
}

4.feign

@FeignClient("seata-account-service")//想要调用的微服务名
public interface AccountService {

    /**
     * 用户支付
     * @param userId
     * @param money
     */
    @PostMapping("/account/payment")
    public CommonResult payment(@RequestParam("userId") Long userId, @RequestParam("money") BigDecimal money);
}
@FeignClient("seata-storage-service")
public interface StorageService {
    /**
     * 订单状态修改
     * @param productId
     * @param count
     */
    @PostMapping("/storage/update")
    public CommonResult update(@RequestParam("productId") Long productId, @RequestParam("count") Integer count);
}

5.controller

@RestController
public class OrderController {

    @Autowired
    private OrderService orderService;

    @PostMapping("/order/create")
    //全局事务(TM向TC端发起事务请求返回xid并依次在需要调用的微服务之间传递(RM))
    @GlobalTransactional(name="tx-order-fsp",rollbackFor = Exception.class)
    public CommonResult createOrder(Order order){
        orderService.createOrder(order);
        return new CommonResult(200,"订单创建成功!!!!!");
    }
}

在微服务调用链路中,的最前面一个微服务(或者需要最先操作数据库的那个微服务上注上GlobalTransactional此时这个地方相当于TM向TC发起事务请求)
6.xml映射文件

<?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.ljc.springcloud.dao.OrderDao">

    <resultMap id="OrderMap" type="com.ljc.springcloud.domain.Order">
        <id property="id" column="id" jdbcType="BIGINT"/>
        <result property="userId" column="userId" jdbcType="BIGINT"/>
        <result property="productId" column="productId" jdbcType="BIGINT"/>
        <result property="count" column="count" jdbcType="INTEGER"/>
        <result property="money" column="money" jdbcType="DECIMAL"/>
        <result property="status" column="status" jdbcType="INTEGER"/>
    </resultMap>

    <insert id="createOrder">
        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>

7.主启动

@SpringBootApplication//取消数据库的自动装配使用自己的seata代理
@EnableFeignClients
@EnableDiscoveryClient
@MapperScan({"com.ljc.springcloud.dao"})
public class OrderMain2001 {
    public static void main(String[] args) {
        ConfigurableApplicationContext run = SpringApplication.run(OrderMain2001.class, args);
    }
}

8.yml
与以上相同只是端口和服务名不同

10.测试

原数据库数据
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
正常测试
在这里插入图片描述
测试结果
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
异常启动
手动在account服务中添加一个by/zero异常
在这里插入图片描述
重启account服务
同样的请求在访问一次

结果
在这里插入图片描述
控制台信息
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
数据库
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
全局事务成功!!!!!

11原理

术语
TC (Transaction Coordinator) - 事务协调者
维护全局和分支事务的状态,驱动全局事务提交或回滚。

TM (Transaction Manager) - 事务管理器
定义全局事务的范围:开始全局事务、提交或回滚全局事务。

RM (Resource Manager) - 资源管理器
管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。

seata有许多模式 AT、TCC、SAGA 和 XA 事务模式

默认是AT模式
AT:提供无侵入自动补偿的事务模式,目前已支持 MySQL、 Oracle 、PostgreSQL和 TiDB的AT模式,H2 开发中

TM向TC端发起事务请求返回xid并依次在需要调用的微服务之间传播

TM 请求 TC 开启一个全局事务。TC 会生成一个 XID 作为该全局事务的编号。 XID,会在微服务的调用链路中传播,保证将多个微服务的子事务关联在一起。 RM 请求 TC 将本地事务注册为全局事务的分支事务,通过全局事务的 XID 进行关联。 TM 请求 TC 告诉 XID 对应的全局事务是进行提交还是回滚。 TC 驱动 RM 们将 XID 对应的自己的本地事务进行提交还是回滚。

在这里插入图片描述

脏写概念:在高并发情况下可能一个元组被多次修改导致,数据库实际数据和后置数据不同。

在执行sql之前会有一个前置镜像在执行之后会有一个后置镜像然后加上一个行锁
当执行成功时会删除前置镜像和后置镜像以及行锁完成提交。
但当执行失败那么会把数据库当前数据与后置镜像想对比若一致则说明没有被脏写回滚,否则只能人工处理。

前置镜像和后置镜像类比一下

--前
select money from account where uid = 1; --结果 1000
--需要执行的sql
update account set money = money - 100 where uid = 1; 
--后
select money from account where uid = 1 ; --结果 900

当发生异常时,当数据库中是900且 后置也是900时说明没有被脏写,那么回滚。将数据库数据改为前置数据1000

学习自:尚硅谷周阳老师springcloud2020,以及seata.io官方文档以及官方博客。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值