分布式事务解决方案 - Seata 使用样例

分布式事务解决方案 - Seata 使用样例

Seata Server端环境准备

(1)从官网上下载seata server端的程序包

下载地址:https://github.com/seata/seata/releases

(2)修改配置

我们是基于file的方式启动注册和承载配置的

1612147785304

打开conf/file.conf文件

修改service 节点目录内容如下:

service {
  #vgroup->rgroup
  vgroup_mapping.my_test_tx_group = "default"
  #only support single node
  default.grouplist = "127.0.0.1:8091"
  #degrade current not support
  enableDegrade = false
  #disable
  disable = false
  #unit ms,s,m,h,d represents milliseconds, seconds, minutes, hours, days, default permanent
  max.commit.retry.timeout = "-1"
  max.rollback.retry.timeout = "-1"
}

说明:需要修改default.grouplist = “127.0.0.1:8091”,将该值设置为seata server向外提供服务ip及端口(或域名+端口)

(4)启动server

到bin目录下执行脚本启动seata server端,注:windows下执行seata-server.bat启动;linux下执行seata-server.sh启动

2.4 项目集成seata

2.4.1 创建日志表undo_log

分别在leadnews_article、leadnews_user、leadnews_wemedia三个库中都创建undo_log表

2.4.2 导入依赖包

因为有多个工程都需要引入seata,所以新建一个工程heima-leadnews-seata专门来处理分布式事务

<dependencies>
    <dependency>
        <groupId>com.heima</groupId>
        <artifactId>heima-leadnews-common</artifactId>
    </dependency>
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
        <version>2.1.0.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>io.seata</groupId>
        <artifactId>seata-all</artifactId>
        <version>0.9.0</version>
        <exclusions>
            <exclusion>
                <groupId>com.alibaba</groupId>
                <artifactId>druid</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.1.21</version>
    </dependency>
</dependencies>

2.4.3 创建代理数据源

(1)因为多个工程都需要依赖与seata,所以在heima-leadnews-seata模块下创建seata的配置类

package com.heima.seata.config;

import com.alibaba.druid.pool.DruidDataSource;
import com.baomidou.mybatisplus.autoconfigure.MybatisPlusProperties;
import com.baomidou.mybatisplus.core.MybatisConfiguration;
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import io.seata.rm.datasource.DataSourceProxy;
import org.mybatis.spring.transaction.SpringManagedTransactionFactory;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;

import javax.sql.DataSource;

@Configuration
@EnableConfigurationProperties({MybatisPlusProperties.class})
public class DataSourcesProxyConfig {

    @Bean
    @ConfigurationProperties(prefix = "spring.datasource")
    public DataSource druidDataSource() {
        return new DruidDataSource();
    }
    //创建代理数据源

    @Primary//@Primary标识必须配置在代码数据源上,否则本地事务失效
    @Bean
    public DataSourceProxy dataSourceProxy(DataSource druidDataSource) {
        return new DataSourceProxy(druidDataSource);
    }

    private MybatisPlusProperties properties;

    public DataSourcesProxyConfig(MybatisPlusProperties properties) {
        this.properties = properties;
    }

    //替换SqlSessionFactory的DataSource
    @Bean
    public MybatisSqlSessionFactoryBean sqlSessionFactory(DataSourceProxy dataSourceProxy, PaginationInterceptor paginationInterceptor) throws Exception {

        // 这里必须用 MybatisSqlSessionFactoryBean 代替了 SqlSessionFactoryBean,否则 MyBatisPlus 不会生效
        MybatisSqlSessionFactoryBean mybatisSqlSessionFactoryBean = new MybatisSqlSessionFactoryBean();
        mybatisSqlSessionFactoryBean.setDataSource(dataSourceProxy);
        mybatisSqlSessionFactoryBean.setTransactionFactory(new SpringManagedTransactionFactory());

        mybatisSqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver()
                .getResources("classpath*:/mapper/*.xml"));

        MybatisConfiguration configuration = this.properties.getConfiguration();
        if(configuration == null){
            configuration = new MybatisConfiguration();
        }
        mybatisSqlSessionFactoryBean.setConfiguration(configuration);

        //设置分页
        Interceptor[] plugins = {paginationInterceptor};
        mybatisSqlSessionFactoryBean.setPlugins(plugins);


        return mybatisSqlSessionFactoryBean;
    }
}

(2)分别在heima-leadnews-article、heima-leadnews-user、heima-leadnews-wemedia引入heima-leadnews-seata工程,并且添加一下配置类:

@Configuration
@ComponentScan("com.heima.seata.config")
public class SeataConfig {

}

2.4.4 配置seata-server链接和注册中心信息

修改注册中心配置,在每个项目中必须按照下方要求来

将配置文件file.conf和配置文件register.conf放到每个需要参与分布式事务项目的resources中。

  • file.conf中的service.default.grouplist修改成seata-server的IP地址file.conf中的
  • service.vgroup_mapping.xxx改成vgroup_mapping.#{spring.application.name}_tx_group = “default”

特别注意:#{spring.application.name}是一个变量,指的是该项目的名称

如自媒体微服务名称的项目名称如下:

1612147815398

那么其配置就是vgroup_mapping.leadnews-wemedia_tx_group = "default"

其他项目也是这么依次配置

2.4.5 指定事务分组

分别在heima-leadnews-article、heima-leadnews-user、heima-leadnews-wemedia微服务的application.yml文件中添加如下配置:

spring:
  cloud:
    alibaba:
      seata:
        tx-service-group: ${spring.application.name}_tx_group

2.4.6 在分布式事务控制方法上添加注解@GlobalTransactional

在ApUserRealnameServiceImpl类的updateStatusById方法上加上@GlobalTransactional注解

2.4.7 启动seata-server

运行:/seata/bin/seata-server.bat

1612147833840

2.4.8 测试

(1)功能测试,看功能能否正常执行。

(2)异常测试,我们在方法中添加int x=1/0 ,看认证信息和自媒体用户是否能够回滚。

1612147850821

posted @ 2021-04-03 10:03  60kmph  阅读(2523)  评论(0编辑  收藏  举报