Spring Cloud 整合Seata

简介: Spring Cloud 整合Seata

1.1 环境准备

1)指定nacos作为配置中心和注册中心

修改registry.conf文件



注意:客户端配置registry.conf使用nacos时也要注意group要和seata server中的group一致,默认group是"DEFAULT_GROUP"

2)同步seata server的配置到nacos

获取/seata/script/config-center/config.txt,修改配置信息

配置事务分组, 要与客户端配置的事务分组一致(客户端properties配置:spring.cloud.alibaba.seata.tx‐service‐group=my_test_tx_group)


配置参数同步到Nacos

shell:

sh ${SEATAPATH}/script/config-center/nacos/nacos-config.sh -h localhost -p 8848 -g SEATA_GROUP -t 5a3c7d6c-f497-4d68-a71a-2e5e3340b3ca

参数说明:

-h: host,默认值 localhost

-p: port,默认值 8848

-g: 配置分组,默认值为 'SEATA_GROUP'

-t: 租户信息,对应 Nacos 的命名空间ID字段, 默认值为空 ''

3) 启动Seata Server

启动Seata Server命令

bin/seata-server.sh

启动成功,默认端口8091


在注册中心中可以查看到seata-server注册成功


2.  Seata如何整合到Spring Cloud微服务

业务场景:

用户下单,整个业务逻辑由三个微服务构成:

  • 仓储服务:对给定的商品扣除库存数量。
  • 订单服务:根据采购需求创建订单。
  • 服务:从用户中扣除余额。




环境准备:

seata:v1.4.0

spring cloud&spring cloud alibaba:


<spring-cloud.version>Greenwich.SR3</spring-cloud.version><spring-cloud-alibaba.version>2.1.1.RELEASE</spring-cloud-alibaba.version>

注意版本选择问题:

spring cloud alibaba 2.1.2 及其以上版本使用seata1.4.0会出现如下异常 (支持seata 1.3.0)


2.1  导入依赖

<!-- 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.4.0</version>
</dependency>
<!--nacos 注册中心-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.1.21</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
    <version>8.0.16</version>
</dependency>
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.1.1</version>
</dependency>

2.2   微服务对应数据库中添加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,
  PRIMARY KEY (`id`),
  UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

2.3  微服务需要使用seata DataSourceProxy代理自己的数据源

/**
 * @author jiuge
 *
 * 需要用到分布式事务的微服务都需要使用seata DataSourceProxy代理自己的数据源
 */
@Configuration
@MapperScan("com.jiuge.datasource.mapper")
public class MybatisConfig {
    /**
     * 从配置文件获取属性构造datasource,注意前缀,这里用的是druid,根据自己情况配置,
     * 原生datasource前缀取"spring.datasource"
     *
     * @return
     */
    @Bean
    @ConfigurationProperties(prefix = "spring.datasource")
    public DataSource druidDataSource() {
        DruidDataSource druidDataSource = new DruidDataSource();
        return druidDataSource;
    }
    /**
     * 构造datasource代理对象,替换原来的datasource
     * @param druidDataSource
     * @return
     */
    @Primary
    @Bean("dataSource")
    public DataSourceProxy dataSourceProxy(DataSource druidDataSource) {
        return new DataSourceProxy(druidDataSource);
    }
    @Bean(name = "sqlSessionFactory")
    public SqlSessionFactory sqlSessionFactoryBean(DataSourceProxy dataSourceProxy) throws Exception {
        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
        //设置代理数据源
        factoryBean.setDataSource(dataSourceProxy);
        ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        factoryBean.setMapperLocations(resolver.getResources("classpath*:mybatis/**/*-mapper.xml"));
        org.apache.ibatis.session.Configuration configuration=new org.apache.ibatis.session.Configuration();
        //使用jdbc的getGeneratedKeys获取数据库自增主键值
        configuration.setUseGeneratedKeys(true);
        //使用列别名替换列名
        configuration.setUseColumnLabel(true);
        //自动使用驼峰命名属性映射字段,如userId ---> user_id
        configuration.setMapUnderscoreToCamelCase(true);
        factoryBean.setConfiguration(configuration);
        return factoryBean.getObject();
    }
}

注意:启动类上需要排除DataSourceAutoConfiguration,否则会出现循环依赖的问题


启动类排除DataSourceAutoConfiguration.class

@SpringBootApplication(scanBasePackages = "com.jiuge",exclude = DataSourceAutoConfiguration.class)
public class AccountServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(AccountServiceApplication.class, args);
    }
}

4. 添加seata的配置

1)将registry.conf文件拷贝到resources目录下,指定注册中心和配置中心都是nacos

registry {
  # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
  type = "nacos"
  nacos {
    serverAddr = "192.168.31.122:8848"
    namespace = ""
    cluster = "default"
    group = "SEATA_GROUP"
  }
}
config {
  # file、nacos 、apollo、zk、consul、etcd3、springCloudConfig
  type = "nacos"
  nacos {
    serverAddr = "192.168.31.122:8848"
    namespace = "29ccf18e-e559-4a01-b5d4-61bad4a89ffd"
    group = "SEATA_GROUP"
  }
}

`org.springframework.cloud:spring-cloud-starter-alibaba-seata``org.springframework.cloud.alibaba.seata.GlobalTransactionAutoConfiguration`类中,默认会使用 `${spring.application.name}-seata-service-group`作为服务名注册到 Seata Server上,如果和service.vgroup_mapping配置不一致,会提示 `no available server to connect`错误

也可以通过配置 `spring.cloud.alibaba.seata.tx-service-group`修改后缀,但是必须和`file.conf`中的配置保持一致


2)在yml中指定事务分组(和配置中心的service.vgroup_mapping 配置一一对应)

spring:
  application:
    name: account-service
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
    alibaba:
      seata:
        tx-service-group:
          my_test_tx_group  # seata 服务事务分组

参考源码:

io.seata.core.rpc.netty.NettyClientChannelManager#getAvailServerList

》NacosRegistryServiceImpl#lookup

》String clusterName = getServiceGroup(key);  #获取seata server集群名称

》List<Instance> firstAllInstances = getNamingInstance().getAllInstances(getServiceName(), getServiceGroup(), clusters)


spring cloud alibaba 2.1.4 之后支持yml中配置seata属性,可以用来替换registry.conf文件

配置支持实现在seata-spring-boot-starter.jar中,也可以引入依赖

<dependency>
    <groupId>io.seata</groupId>
    <artifactId>seata-spring-boot-starter</artifactId>
    <version>1.4.0</version>
</dependency>

在yml中配置

seata:
  # seata 服务分组,要与服务端nacos-config.txt中service.vgroup_mapping的后缀对应
  tx-service-group: my_test_tx_group
  registry:
    # 指定nacos作为注册中心
    type: nacos
    nacos:
      server-addr: 127.0.0.1:8848
      namespace: ""
      group: SEATA_GROUP  
  config:
    # 指定nacos作为配置中心
    type: nacos
    nacos:
      server-addr: 127.0.0.1:8848
      namespace: "54433b62-df64-40f1-9527-c907219fc17f"
      group: SEATA_GROUP

3) 在事务发起者中添加@GlobalTransactional注解

核心代码

@Override
//@Transactional
@GlobalTransactional(name="createOrder")
public Order saveOrder(OrderVo orderVo){
    log.info("=============用户下单=================");
    log.info("当前 XID: {}", RootContext.getXID());
    // 保存订单
    Order order = new Order();
    order.setUserId(orderVo.getUserId());
    order.setCommodityCode(orderVo.getCommodityCode());
    order.setCount(orderVo.getCount());
    order.setMoney(orderVo.getMoney());
    order.setStatus(OrderStatus.INIT.getValue());
    Integer saveOrderRecord = orderMapper.insert(order);
    log.info("保存订单{}", saveOrderRecord > 0 ? "成功" : "失败");
    //扣减库存
    storageFeignService.deduct(orderVo.getCommodityCode(),orderVo.getCount());
    //扣减余额
    accountFeignService.debit(orderVo.getUserId(),orderVo.getMoney());
    //更新订单
    Integer updateOrderRecord = orderMapper.updateOrderStatus(order.getId(),OrderStatus.SUCCESS.getValue());
    log.info("更新订单id:{} {}", order.getId(), updateOrderRecord > 0 ? "成功" : "失败");
    return order;
}

4)测试分布式事务是否生效

用户下单账户余额不足,库存是否回滚

至此,springcloud alibaba 整合seata的实例完成。

目录
相关文章
|
22天前
|
Java 关系型数据库 数据库
微服务SpringCloud分布式事务之Seata
SpringCloud+SpringCloudAlibaba的Seata实现分布式事务,步骤超详细,附带视频教程
49 1
|
6月前
|
NoSQL Java Nacos
SpringCloud集成Seata并使用Nacos做注册中心与配置中心
SpringCloud集成Seata并使用Nacos做注册中心与配置中心
232 3
|
1月前
|
消息中间件 监控 Java
如何将Spring Boot + RabbitMQ应用程序部署到Pivotal Cloud Foundry (PCF)
如何将Spring Boot + RabbitMQ应用程序部署到Pivotal Cloud Foundry (PCF)
44 6
|
1月前
|
Java 关系型数据库 MySQL
如何将Spring Boot + MySQL应用程序部署到Pivotal Cloud Foundry (PCF)
如何将Spring Boot + MySQL应用程序部署到Pivotal Cloud Foundry (PCF)
70 5
|
1月前
|
缓存 监控 Java
如何将Spring Boot应用程序部署到Pivotal Cloud Foundry (PCF)
如何将Spring Boot应用程序部署到Pivotal Cloud Foundry (PCF)
51 5
|
2月前
|
存储 Java 关系型数据库
在Spring Boot中整合Seata框架实现分布式事务
可以在 Spring Boot 中成功整合 Seata 框架,实现分布式事务的管理和处理。在实际应用中,还需要根据具体的业务需求和技术架构进行进一步的优化和调整。同时,要注意处理各种可能出现的问题,以保障分布式事务的顺利执行。
138 6
|
4月前
|
SQL NoSQL 数据库
SpringCloud基础6——分布式事务,Seata
分布式事务、ACID原则、CAP定理、Seata、Seata的四种分布式方案:XA、AT、TCC、SAGA模式
SpringCloud基础6——分布式事务,Seata
|
6月前
|
负载均衡 Java Spring
Spring cloud gateway 如何在路由时进行负载均衡
Spring cloud gateway 如何在路由时进行负载均衡
717 15
|
5月前
|
关系型数据库 MySQL 数据库
SpringCloud2023中使用Seata解决分布式事务
对于分布式系统而言,需要保证分布式系统中的数据一致性,保证数据在子系统中始终保持一致,避免业务出现问题。分布式系统中对数据的操作要么一起成功,要么一起失败,必须是一个整体性的事务。Seata简化了这个使用过程。
115 2
|
6月前
|
Java Spring
spring cloud gateway在使用 zookeeper 注册中心时,配置https 进行服务转发
spring cloud gateway在使用 zookeeper 注册中心时,配置https 进行服务转发
148 3