Seata(分布式事务解决方案)快速开始

在这里插入图片描述

版本

  • SpringBoot:2.2.5.RELEASE
  • SpringCloud:Hoxton.SR3
  • SpringCloudAlibaba:2.2.1.RELEASE
  • Nacos Server:1.2.1
  • Seata Server:1.1.0
  • Jdk:1.8

部署

0:下载Nacos Server和Seata Server

https://github.com/alibaba/nacos/releases
https://github.com/seata/seata/releases

1:部署Nacos Server

Nacos快速开始:https://blog.csdn.net/momo57l/article/details/104298206
Nacos集群部署:https://blog.csdn.net/momo57l/article/details/104737165

2:部署Seata Server
第一步:修改conf/registry.conf:注册中心和配置中心都改为使用nacos,serverAddr填nacos地址,namespace是nacos中的命名空间,默认不填是default,config下的group是nacos中的分组,默认是SEATA_GROUP,也可以改成其他分组

registry {
  type = "nacos"

  nacos {
    serverAddr = "localhost"
    namespace = "90f980dc-c419-4be2-95a2-85d5c73deccb"
    cluster = "default"
  }
}

config {
  type = "nacos"

  nacos {
    serverAddr = "localhost"
    namespace = "90f980dc-c419-4be2-95a2-85d5c73deccb"
    group = "SEATA_GROUP"
  }
}

第二步:下载所需脚本文件:都在github script文件夹下,需要下载维护seata事务信息的mysql.sqlseata配置文件config.txt将seata配置文件导入nacos的导入脚本
下载完成后将会得到三个文件:mysql.sql、config.txt和nacos-config.sh(或者nacos-config.py)

第三步:修改config.txt

# 将存储seata事务信息的方式改为db存储
store.mode=db
# 改为新建或者现有数据库配置信息
store.db.datasource=dbcp
store.db.dbType=mysql
store.db.driverClassName=com.mysql.jdbc.Driver
store.db.url=jdbc:mysql://127.0.0.1:3306/db_seata_0420?useUnicode=true
store.db.user=root
store.db.password=root
store.db.minConn=1
store.db.maxConn=3
store.db.globalTable=global_table
store.db.branchTable=branch_table
store.db.queryLimit=100
store.db.lockTable=lock_table

第四步:执行mysql.sql:在第三步配置的数据库中执行SQL文件后得到三张表,分别是lock_table、branch_table、global_table

第五步:将config.txt导入至nacos:我这里是window环境,所以使用的是nacos-config.py这个脚本,在控制台执行python .\nacos-config.py localhost:8848即可完成导入,Linux环境可以使用nacos-config.sh脚本,导入过程有问题的话建议先打开导入脚本看下config.txt目录是否正确。数据导入到nacos后是在默认的命名空间

第六步:将导入至nacos中的config.txt信息迁移至对应的命名空间(namespace):在第一步中registry.conf文件里填写了对应的namespace,如果是默认的则可以跳过该步骤。迁移方式是先导出再到对应的命名空间中执行导入,然后删除之前命名空间中的配置信息即可,注意要将SEATA_GROUP的配置信息全部勾选
在这里插入图片描述
第七步:启动Seata Server:windows环境下直接在控制台执行.\seata-server.bat即可启动。Linux环境下可以使用seata-server.sh -h 127.0.0.1 -p 8091 -m db -n 1 -e test指令启动。启动完成后在nacos服务列表中就可以看到seata服务了

  • -h: 注册到注册中心的ip
  • -p: Server rpc 监听端口
  • -m: 全局事务会话信息存储模式,file、db,优先读取启动参数
  • -n: Server node,多个Server时,需区分各自节点,用于生成不同区间的transactionId,以免冲突
  • -e: 多环境配置参考 http://seata.io/en-us/docs/ops/multi-configuration-isolation.html

在这里插入图片描述

食用

0:项目搭建
第一步:引入相关依赖,包括jpa、nacos、seata、web等

# nacos只需要服务发现的客户端,如果要使用配置中心的话可以再引入nacos-config
<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>
</dependency>

第二步:创建两个微服务,分别是demo-orde(订单服务)和demo-payment(支付服务),支付服务中有创建支付单业务,订单服务中有创建订单业务,创建订单操作会通过feign client调用支付服务中的创建支付单业务,两个业务分别处于各自本地事务中

订单服务中的创建订单

/**
 * 创建订单业务,会通过feign client调用支付服务中的创建支付单业务
 * 为了方便测试,orderBo中有个flag字段,可以控制下面这个异常是否执行
 */
@GlobalTransactional
@Transactional(rollbackOn = Exception.class)
public void createOrder(OrderBO orderBo) {
    OrderPO po = new OrderPO();
    po.setOrderCode("TEST" + System.currentTimeMillis());
    po.setOrderAmount(orderBo.getOrderAmount());
    orderRepository.save(po);

    paymentFeignClient.createOrderPayment(OrderPaymentBO.builder()
            .orderId(po.getId())
            .orderAmount(orderBo.getOrderAmount())
            .build());

    if (orderBo.getFlag()) {
        throw new RuntimeException("demo order exception.");
    }
}

支付服务中的创建支付单业务

/**
 * 支付服务中的创建支付单业务
 */
@Transactional(rollbackOn = Exception.class)
public void createOrderPayment(OrderPaymentBO orderPaymentBo) {
    OrderPaymentPO po = new OrderPaymentPO();
    po.setOrderId(orderPaymentBo.getOrderId());
    po.setOrderAmount(orderPaymentBo.getOrderAmount());
    orderPaymentRepository.save(po);
}

主要配置信息,注意nacos配置下的namespace要跟之前配置的namespace一致。seata配置下的tx-service-group需要手动指定,其中service.vgroup-mapping.${tx-service-group}配置成default

server:
  port: 8095

seata:
  tx-service-group: ${spring.application.name}
  service:
    vgroup-mapping:
      demo-order: default

spring:
  application:
    name: demo-order

  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
        namespace: 90f980dc-c419-4be2-95a2-85d5c73deccb

第三步:测试,如果两个业务分别处于各自本地事务,在创建订单业务中,如果通过feign client调用支付服务之后发生异常的话,支付服务的业务是不会回滚的

POST localhost:8095/order/create-order
{
	"orderAmount":100,
	"flag":true
}

结果跟预期一致,订单表没有新增记录,支付单新增了一条记录

第四步:引入seata,只需要在订单业务上加上@GlobalTransactional注解即可,这时两个业务将处于同一个全局事务中,如果订单调用支付后发生异常,则两者都会回滚

@GlobalTransactional
@Transactional(rollbackOn = Exception.class)
public void createOrder(OrderBO orderBo) {
    OrderPO po = new OrderPO();
    po.setOrderCode("TEST" + System.currentTimeMillis());
    po.setOrderAmount(orderBo.getOrderAmount());
    orderRepository.save(po);

    paymentFeignClient.createOrderPayment(OrderPaymentBO.builder()
            .orderId(po.getId())
            .orderAmount(orderBo.getOrderAmount())
            .build());

    if (orderBo.getFlag()) {
        throw new RuntimeException("demo order exception.");
    }
}

此时异常发生后,两个业务都会回滚

补充:SEATA AT 模式需要 UNDO_LOG 表支持,所有我们需要在业务数据库中新增一张UNDO_LOG 表

-- 注意此处0.3.0+ 增加唯一索引 ux_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;

链接

Seata:http://seata.io/zh-cn/
Spring Cloud Alibaba:https://spring.io/projects/spring-cloud-alibaba

案例

Gitee:https://gitee.com/liujiazhongg_admin/seata-nacos-example

发布了36 篇原创文章 · 获赞 19 · 访问量 2416

猜你喜欢

转载自blog.csdn.net/momo57l/article/details/105585563