Seata分布式事务简易项目搭建

Seata分布式事务简易项目搭建

本次记载纯个人记载,方便自己以后查看笔记,有错误或者疑惑的地方可以指出来,我也想进步

项目结构

我用的windows 直接使用 tree simple >> 1.txt 然后改下就成下面了

源码地址:gitee.com/meiyingd/si…

simple
├─simple-storage        # 库存demo项目
├─simple-swagger		# swagger封装项目
├─simple-web-common		# web封装demo项目
├─simple-generator		# 基于mp的代码生成demo项目(网上找的)
├─simple-storage-api	# storage-api
├─simple-business		# 入口demo项目
└─simple-base-common	# 基础封装demo项目
复制代码

技术栈

以后扩展再说

技术 说明 官网
SpringBoot 2.3.5.RELEASE 容器+MVC框架 spring.io/projects/sp…
Mybatis-plus 3.4.1 ORM框架 mybatis.plus
Nacos 动态服务发现、配置管理和服务管理平台 nacos.io
Seata 分布式事务中间件 seata.io/zh-cn

基于idea搭建项目

如果自己已经有了项目可以不用看这个

1. 创建simple

点击 File -> New -> Project, 选择maven,直接点击Next;

image-20210708193034856

录入自己的 项目名和目录名以及相关GAV;

image-20210708193214824

稍等片刻,等项目建立完毕之后,修改pom文件;

<!-- 代码库 -->
<repositories>
    <repository>
        <id>maven-ali</id>
        <url>https://maven.aliyun.com/repository/public</url>
        <releases>
            <enabled>true</enabled>
        </releases>
        <snapshots>
            <enabled>true</enabled>
            <updatePolicy>always</updatePolicy>
            <checksumPolicy>fail</checksumPolicy>
        </snapshots>
    </repository>
</repositories>


<pluginRepositories>
    <pluginRepository>
        <id>public</id>
        <name>aliyun nexus</name>
        <url>https://maven.aliyun.com/repository/public</url>
        <releases>
            <enabled>true</enabled>
        </releases>
        <snapshots>
            <enabled>false</enabled>
        </snapshots>
    </pluginRepository>
</pluginRepositories>
复制代码

右击项目,New -> Moudle… 创建子工程,子工程的创建与父工程创建一致;创建完子项目截图:

image-20210708194344746

2. 添加相关配置

2.1 父工程
2.1.1 pom
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.ph.share</groupId>
    <artifactId>ph-simple</artifactId>
    <packaging>pom</packaging>
    <version>1.0</version>
    <modules>
        <module>simple-base-common</module>
        <module>simple-web-common</module>
        <module>simple-swagger</module>
        <module>simple-generator</module>
        <module>simple-business</module>
        <module>simple-storage</module>
        <module>simple-storage-api</module>
    </modules>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>

        <lombok.version>1.18.16</lombok.version>
        <spring.boot.version>2.3.5.RELEASE</spring.boot.version>
        <spring.cloud.version>Hoxton.SR9</spring.cloud.version>
        <alibaba.cloud.version>2.2.5.RELEASE</alibaba.cloud.version>

        <mybatisplus.boot.starter.version>3.4.1</mybatisplus.boot.starter.version>
        <redisson.version>3.10.1</redisson.version>
        <springfox.boot.starter.version>3.0.0</springfox.boot.starter.version>
        <swagger-bootstrap-ui.version>1.9.2</swagger-bootstrap-ui.version>

        <fastjson.version>1.2.60</fastjson.version>

    </properties>

    <dependencyManagement>
        <dependencies>
            <!--https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-dependencies/2.3.3.RELEASE-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring.boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

            <!--https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-dependencies/Hoxton.SR8-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring.cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

            <!--https://mvnrepository.com/artifact/com.alibaba.cloud/spring-cloud-alibaba-dependencies/2.2.1.RELEASE-->
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>${alibaba.cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

            <!--mybatis plus和springboot整合-->
            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatis-plus-boot-starter</artifactId>
                <version>${mybatisplus.boot.starter.version}</version>
            </dependency>

            <!--分布式锁-->
            <dependency>
                <groupId>org.redisson</groupId>
                <artifactId>redisson</artifactId>
                <version>${redisson.version}</version>
            </dependency>

            <!--接口文档依赖-->
            <dependency>
                <groupId>io.springfox</groupId>
                <artifactId>springfox-boot-starter</artifactId>
                <version>${springfox.boot.starter.version}</version>
            </dependency>

            <dependency>
                <groupId>com.github.xiaoymin</groupId>
                <artifactId>swagger-bootstrap-ui</artifactId>
                <version>${swagger-bootstrap-ui.version}</version>
            </dependency>

            <!--utils-->
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>fastjson</artifactId>
                <version>${fastjson.version}</version>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <scope>provided</scope>
        </dependency>
    </dependencies>

    <!-- 代码库 -->
    <repositories>
        <repository>
            <id>maven-ali</id>
            <url>https://maven.aliyun.com/repository/public</url>
            <releases>
                <enabled>true</enabled>
            </releases>
            <snapshots>
                <enabled>true</enabled>
                <updatePolicy>always</updatePolicy>
                <checksumPolicy>fail</checksumPolicy>
            </snapshots>
        </repository>
    </repositories>


    <pluginRepositories>
        <pluginRepository>
            <id>public</id>
            <name>aliyun nexus</name>
            <url>https://maven.aliyun.com/repository/public</url>
            <releases>
                <enabled>true</enabled>
            </releases>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </pluginRepository>
    </pluginRepositories>

    <!--module不用添加打包版本信息-->
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.0</version>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>${spring.boot.version}</version>
                <configuration>
                    <fork>true</fork>
                    <addResources>true</addResources>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>
复制代码
2.2 simple-base-common
2.2.1 pom
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>ph-simple</artifactId>
        <groupId>com.ph.share</groupId>
        <version>1.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>simple-base-common</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>${fastjson.version}</version>
        </dependency>
    </dependencies>
</project>
复制代码
2.2.2 WrapperResponse

新建包com.ph.web.common.response

package com.ph.web.common.response;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class WrapperResponse<T> implements Serializable{

    private static final long serialVersionUID = 1L;

    /**
     * 状态码 0 表示成功
     */

    private Integer code;
    /**
     * 数据
     */
    private T data;
    /**
     * 描述
     */
    private String msg;


    /**
     *  获取远程调用数据
     *  注意事项:
     *      支持多单词下划线专驼峰(序列化和反序列化)
     *
     *
     * @param typeReference 转换类
     * @param <T> 转换数据
     * @return 返回响应
     */
    public static  <T> T getData(TypeReference<T> typeReference, T data){
        return JSON.parseObject(JSON.toJSONString(data),typeReference);
    }

    /**
     * 成功,不传入数据
     * @return 返回响应
     */
    public static <T> WrapperResponse<T> buildSuccess() {
        return new WrapperResponse<>(0, null, null);
    }

    /**
     *  成功,传入数据
     * @param data 正确响应数据
     * @return 返回响应
     */
    public static <T> WrapperResponse<T> buildSuccess(T data) {
        return new WrapperResponse<>(0, data, null);
    }

    /**
     * 失败,传入描述信息
     * @param msg 提示文本
     * @return 返回错误响应
     */
    public static <T> WrapperResponse<T> buildError(String msg) {
        return new WrapperResponse<>(-1, null, msg);
    }



    /**
     * 自定义状态码和错误信息
     * @param code 提示编码
     * @param msg 提示文本
     * @return 返回响应
     */
    public static <T> WrapperResponse<T> buildCodeAndMsg(int code, String msg) {
        return new WrapperResponse<>(code, null, msg);
    }

}
复制代码
2.3 simple-swagger
2.3.1 pom
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>ph-simple</artifactId>
        <groupId>com.ph.share</groupId>
        <version>1.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>simple-swagger</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <!--接口文档依赖-->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-boot-starter</artifactId>
            <version>${springfox.boot.starter.version}</version>
        </dependency>

        <dependency>
            <groupId>com.github.xiaoymin</groupId>
            <artifactId>swagger-bootstrap-ui</artifactId>
            <version>${swagger-bootstrap-ui.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
            <scope>compile</scope>
            <version>2.2.5.RELEASE</version>
        </dependency>

    </dependencies>

</project>
复制代码
2.3.2 spring.factories
# 此处在resources下新建一个META-INF文件 然后touch一个 spring.factories文件 内容如下
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.ph.swagger.config.SwaggerConfig
复制代码
2.3.3 SwaggerProperties

新建包 com.ph.swagger.config

package com.ph.swagger.config;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;

import java.util.HashMap;
import java.util.Map;

@Data
@ConfigurationProperties(prefix = "swagger")
public class SwaggerProperties {

    private boolean enabled = false;

    /**
     * swagger-ui api 跳转域名
     */
    private String apiDomain;

    /**
     * swagger-ui title
     */
    private String title;

    /**
     * swagger-ui desc
     */
    private String description;

    /**
     * swagger-ui version
     */
    private String version;

    /**
     * swagger-ui scan base package
     */
    private String basePackage;

    /**
     * swagger-ui doc default true
     */
    private boolean doc = true;

    /**
     * swagger-ui apiScanner default false
     */
    private boolean apiScanner = false;

    /**
     * swagger-ui ResourceHandlers - location map
     */
    private Map<String, String> hlMap = new HashMap<>();



}
复制代码
2.3.4 SwaggerConfig
package com.ph.swagger.config;

import io.swagger.annotations.Api;
import lombok.AllArgsConstructor;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.oas.annotations.EnableOpenApi;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;

import java.util.Map;

@AllArgsConstructor
@Configuration
@EnableOpenApi
@EnableConfigurationProperties(SwaggerProperties.class)
@ConditionalOnProperty(name = "swagger.enabled", havingValue = "true")
public class SwaggerConfig implements WebMvcConfigurer {

    private final SwaggerProperties swaggerProperties;

    @Bean
    public Docket createRestApi() {
        if(swaggerProperties.isApiScanner()) {
            return new Docket(DocumentationType.OAS_30)
                    .apiInfo(apiInfo())
                    .select()
                .apis(RequestHandlerSelectors.withClassAnnotation(Api.class))// 扫描所有有注解的api,用这种方式更灵活
//                    .apis(RequestHandlerSelectors.basePackage(swaggerProperties.getBasePackage()))
                    .paths(PathSelectors.any())
                    .build();
        }
        return new Docket(DocumentationType.OAS_30)
                .apiInfo(apiInfo())
                .select()
//                .apis(RequestHandlerSelectors.withClassAnnotation(Api.class))// 扫描所有有注解的api,用这种方式更灵活
                .apis(RequestHandlerSelectors.basePackage(swaggerProperties.getBasePackage()))
                .paths(PathSelectors.any())
                .build();
    }

    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title(swaggerProperties.getTitle())
                .description(swaggerProperties.getDescription())
                .termsOfServiceUrl(swaggerProperties.getApiDomain())
                .version(swaggerProperties.getVersion())
                .build();
    }

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        Map<String, String> hlMap = swaggerProperties.getHlMap();
        hlMap.forEach((h, l) -> registry.addResourceHandler(h).addResourceLocations(l));
        registry.addResourceHandler("swagger-ui.html")
                .addResourceLocations("classpath:/META-INF/resources/");
        if (swaggerProperties.isDoc()) {
            registry.addResourceHandler("doc.html")
                    .addResourceLocations("classpath:/META-INF/resources/");
        }
        registry.addResourceHandler("/webjars/**")
                .addResourceLocations("classpath:/META-INF/resources/webjars/");
    }
}
复制代码
2.4 simple-web-common
2.4.1 pom
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>ph-simple</artifactId>
        <groupId>com.ph.share</groupId>
        <version>1.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>simple-web-common</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>

        <!--接口文档依赖-->
        <dependency>
            <groupId>com.ph.share</groupId>
            <artifactId>simple-swagger</artifactId>
            <version>1.0</version>
        </dependency>

        <dependency>
            <groupId>com.ph.share</groupId>
            <artifactId>simple-base-common</artifactId>
            <version>1.0</version>
        </dependency>

        <!--boot依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!--添加nacos客户端-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <!--Feign远程调用-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

        <!--redis客户端-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>io.lettuce</groupId>
                    <artifactId>lettuce-core</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
        </dependency>


        <!--redisson分布式锁-->
        <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson</artifactId>
        </dependency>

        <!--alibaba微服务整合分布式事务,这个方式才行-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>io.seata</groupId>
                    <artifactId>seata-spring-boot-starter</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>io.seata</groupId>
            <artifactId>seata-spring-boot-starter</artifactId>
            <version>1.3.0</version>
        </dependency>

        <!--mybatis-plus-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
        </dependency>

        <!--mysql 连接-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
        </dependency>

        <!--用于加密-->
        <dependency>
            <groupId>commons-codec</groupId>
            <artifactId>commons-codec</artifactId>
        </dependency>

    </dependencies>
</project>
复制代码
2.5 simple-generator
2.5.1 pom
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>ph-simple</artifactId>
        <groupId>com.ph.share</groupId>
        <version>1.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>simple-generator</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <!-- 代码自动生成依赖 begin -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>3.4.1</version>
        </dependency>
        <!-- velocity -->
        <dependency>
            <groupId>org.apache.velocity</groupId>
            <artifactId>velocity-engine-core</artifactId>
            <version>2.0</version>
        </dependency>
        <!-- 代码自动生成依赖 end-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
    </dependencies>
</project>
复制代码
2.5.2 MyBatisPlusGenerator

创建包 com.ph.generator

package com.ph.generator;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.GlobalConfig;
import com.baomidou.mybatisplus.generator.config.PackageConfig;
import com.baomidou.mybatisplus.generator.config.StrategyConfig;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;

public class MyBatisPlusGenerator {

    private static final String author = "swallow";
    // storage
    private static final String outputDir = "F:\\learn\\Seate\\simple\\simple-storage\\src\\main\\java";
    private static final String[] tables = new String[] {"t_storage"};
    private static final String parentPackage = "com.ph.storage";
    private static final String driverName = "com.mysql.jdbc.Driver";
    private static final String dbUrl = "jdbc:mysql://ph.base.cn:3306/simple_storage?useSSL=false";

    private static final String dbUsername = "root";
    private static final String dbPassword = "swallow";


    public static void main(String[] args) {
        //1. 全局配置
        GlobalConfig config = new GlobalConfig();
        // 是否支持AR模式
        config.setActiveRecord(true)
                // 作者
                .setAuthor(author)
                // 生成路径,最好使用绝对路径,window路径是不一样的
                //TODO  TODO  TODO  TODO
                .setOutputDir(outputDir)
                // 文件覆盖
                .setFileOverride(true)
                // 主键策略
                .setIdType(IdType.AUTO)

                .setDateType(DateType.ONLY_DATE)
                // 设置生成的service接口的名字的首字母是否为I,默认Service是以I开头的
                .setServiceName("%sService")

                //实体类结尾名称
                .setEntityName("%sDO")

                //生成基本的resultMap
                .setBaseResultMap(true)

                //不使用AR模式
                .setActiveRecord(false)

                //生成基本的SQL片段
                .setBaseColumnList(true);

        //2. 数据源配置
        DataSourceConfig dsConfig = new DataSourceConfig();
        // 设置数据库类型
        dsConfig.setDbType(DbType.MYSQL)
                .setDriverName(driverName)
                //TODO  TODO  TODO  TODO
                .setUrl(dbUrl)
                .setUsername(dbUsername)
                .setPassword(dbPassword);

        //3. 策略配置globalConfiguration中
        StrategyConfig stConfig = new StrategyConfig();

        //全局大写命名
        stConfig.setCapitalMode(true)
                // 去除前缀
                .setTablePrefix("t_")
                // 数据库表映射到实体的命名策略
                .setNaming(NamingStrategy.underline_to_camel)

                //使用lombok
                .setEntityLombokModel(true)

                //使用restcontroller注解
                .setRestControllerStyle(true)

                // 生成的表, 支持多表一起生成,以数组形式填写
                //TODO  TODO  TODO  TODO
                .setInclude(tables);

        //4. 包名策略配置
        PackageConfig pkConfig = new PackageConfig();
        pkConfig.setParent(parentPackage)
                .setMapper("mapper")
                .setService("service")
                .setController("controller")
                .setEntity("model")
                .setXml("mapper");

        //5. 整合配置
        AutoGenerator ag = new AutoGenerator();
        ag.setGlobalConfig(config)
                .setDataSource(dsConfig)
                .setStrategy(stConfig)
                .setPackageInfo(pkConfig);

        //6. 执行操作
        ag.execute();
        System.out.println("======= Done 相关代码生成完毕  ========");
    }
}
复制代码
2.6 simple-storage-api
2.6.1 pom
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>ph-simple</artifactId>
        <groupId>com.ph.share</groupId>
        <version>1.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>simple-storage-api</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>com.ph.share</groupId>
            <artifactId>simple-base-common</artifactId>
            <version>1.0</version>
        </dependency>
        <!--Feign远程调用-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
            <scope>provided</scope>
        </dependency>
    </dependencies>
</project>
复制代码
2.6.2 CommodityDTO

创建包 com.ph.storage.dto

package com.ph.storage.dto;

import lombok.Data;
import lombok.ToString;
import lombok.experimental.Accessors;

import java.io.Serializable;

@Data
@ToString
@Accessors(chain = true)
public class CommodityDTO implements Serializable {

    private static final long serialVersionUID = 1L;

    private Integer id;

    private String commodityCode;

    private String name;

    private Integer count;

}
复制代码
2.6.3 StorageFeignService

创建包 com.ph.storage.service

package com.ph.storage.service;

import com.ph.storage.dto.CommodityDTO;
import com.ph.web.common.response.WrapperResponse;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@FeignClient(value = "ph-simple-storage", path = "/")
public interface StorageFeignService {
    @RequestMapping(value = "/api/storage/dec_storage", method = RequestMethod.POST)
    WrapperResponse<Object> decreaseStorage(@RequestBody CommodityDTO commodityDTO);
}
复制代码
2.7 simple-storage
2.7.1 pom
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>ph-simple</artifactId>
        <groupId>com.ph.share</groupId>
        <version>1.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>simple-storage</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>com.ph.share</groupId>
            <artifactId>simple-storage-api</artifactId>
            <version>1.0</version>
        </dependency>
        <dependency>
            <groupId>com.ph.share</groupId>
            <artifactId>simple-web-common</artifactId>
            <version>1.0</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>
复制代码
2.7.2 application.yml
# simple系列都是90开头
server:
  port: 9003
# swagger配置
swagger:
  api-domain: https://storage.simple.com
  doc: false
  enabled: true
  description: ${swagger.title}
  base-package: com.ph.storage
  title: 仓库api
  version: 1.0

spring:
  application:
    name: ph-simple-storage
  # 此处文件上传限制
  servlet:
    multipart:
      max-file-size: 20MB
      max-request-size: 20MB
  #数据库配置 默认数据库连接池
  datasource:
    #配置数据源
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://ph.base.cn:3306/simple_storage?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
    username: root
    password: swallow
  cloud:
    #注册中心地址
    nacos:
      discovery:
        server-addr: ph.base.cn:8848
  main:
    allow-bean-definition-overriding: true
feign:
  client:
    config:
      default:
        connect-timeout: 5000
        read-timeout: 20000

mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  mapper-locations: classpath:mapper/*.xml

#设置日志级别,ERROR/WARN/INFO/DEBUG,默认是INFO以上才显示
logging:
  level:
    com.ph: DEBUG
    root: INFO
    io:
      seata:
        INFO

seata:
  application-id: ph-simple-storage
  tx-service-group: my_test_tx_group
  service:
    vgroup-mapping:
      my_test_tx_group: default
    grouplist:
#      default: ph.base.cn:8091
      default: localhost:8091
  # 关闭自动代理 继承了 mybatis-plus 且使用的是 seata-spring-boot-starter 使用下面方法关闭  问题地址见 http://seata.io/zh-cn/docs/overview/faq.html Q: 25. 使用mybatis-plus 动态数据源组件后undolog无法删除 ?
  enable-auto-data-source-proxy: false
  registry:
    type: nacos
    nacos:
      server-addr: ph.base.cn:8848
  config:
    type: nacos
    nacos:
      server-addr: ph.base.cn:8848
复制代码
2.7.3 StorageApplication

创建包 com.ph.storage

package com.ph.storage;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;

@SpringBootApplication(scanBasePackages = {"com.ph"})
@EnableFeignClients(basePackages = {"com.ph"})
public class StorageApplication {

    public static void main(String[] args) {
        SpringApplication.run(StorageApplication.class, args);
    }

}
复制代码
2.7 4 SeataAutoConfig

创建包 com.ph.storage.config

package com.ph.storage.config;

import com.alibaba.druid.pool.DruidDataSource;
import io.seata.rm.datasource.DataSourceProxy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

import javax.sql.DataSource;

@Configuration
public class SeataAutoConfig {

    private final DataSourceProperties dataSourceProperties;
    private final static Logger logger = LoggerFactory.getLogger(SeataAutoConfig.class);
    private DataSourceProxy dataSourceProxy;

    public SeataAutoConfig(DataSourceProperties dataSourceProperties) {
        this.dataSourceProperties = dataSourceProperties;
    }

    @Bean(name = "dataSource") // 声明其为Bean实例
    @Primary // 在同样的DataSource中,首先使用被标注的DataSource
    public DataSource druidDataSource() {
        DruidDataSource druidDataSource = new DruidDataSource();
        logger.info("dataSourceProperties.getUrl():{}", dataSourceProperties.getUrl());
        druidDataSource.setUrl(dataSourceProperties.getUrl());
        druidDataSource.setUsername(dataSourceProperties.getUsername());
        druidDataSource.setPassword(dataSourceProperties.getPassword());
        druidDataSource.setDriverClassName(dataSourceProperties.getDriverClassName());
        druidDataSource.setInitialSize(0);
        druidDataSource.setMaxActive(180);
        druidDataSource.setMaxWait(60000);
        druidDataSource.setMinIdle(0);
        druidDataSource.setValidationQuery("Select 1 from DUAL");
        druidDataSource.setTestOnBorrow(false);
        druidDataSource.setTestOnReturn(false);
        druidDataSource.setTestWhileIdle(true);
        druidDataSource.setTimeBetweenEvictionRunsMillis(60000);
        druidDataSource.setMinEvictableIdleTimeMillis(25200000);
        druidDataSource.setRemoveAbandoned(true);
        druidDataSource.setRemoveAbandonedTimeout(1800);
        druidDataSource.setLogAbandoned(true);
        logger.info("装载dataSource........");
        dataSourceProxy = new DataSourceProxy(druidDataSource);
        return dataSourceProxy;
    }

    /**
     * init datasource proxy
     *
     * @Param: druidDataSource datasource bean instance
     * @Return: DataSourceProxy datasource proxy
     */
    @Bean
    public DataSourceProxy dataSourceProxy() {
        logger.info("代理dataSource........");
        return dataSourceProxy;
    }

}
复制代码
2.7.5 MybatisPlusConfig

在config包下加入

package com.ph.storage.config;

import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@MapperScan(value = {"com.ph.storage.mapper"})
public class MybatisPlusConfig {

    /**
     * mybatis-plus分页插件<br>
     * 文档:http://mp.baomidou.com<br>
     */
    @Bean
    public PaginationInnerInterceptor paginationInterceptor() {
        return new PaginationInnerInterceptor();
    }

}
复制代码
2.7.6 配置好generagor之后 运行生成相关代码

最后生成目录截图,此处的StorageMapper.xml 是从mapper包下复制粘贴过来的并删除原来的xml

image-20210708202511339

2.7.7 StorageMapper.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.ph.storage.mapper.StorageMapper">

    <!-- 通用查询映射结果 -->
    <resultMap id="BaseResultMap" type="com.ph.storage.model.StorageDO">
        <id column="id" property="id" />
        <result column="commodity_code" property="commodityCode" />
        <result column="name" property="name" />
        <result column="count" property="count" />
    </resultMap>

    <!-- 通用查询结果列 -->
    <sql id="Base_Column_List">
        id, commodity_code, name, count
    </sql>

    <update id="decreaseStorage">
        update t_storage set count = count-${count} where commodity_code = #{commodityCode} and count >= ${count}
    </update>
    <select id="loadById" resultType="com.ph.storage.model.StorageDO">
        select
            id id, commodity_code commodityCode, name name, count count
        from t_storage where id = #{id} for update
    </select>

</mapper>

复制代码
2.7.8 StorageMapper
package com.ph.storage.mapper;

import com.ph.storage.model.StorageDO;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Param;

/**
 * <p>
 *  Mapper 接口
 * </p>
 *
 * @author swallow
 * @since 2021-07-08
 */
public interface StorageMapper extends BaseMapper<StorageDO> {

    /**
     * 扣减商品库存
     * @Param: commodityCode 商品code  count扣减数量
     * @Return:
     */
    int decreaseStorage(@Param("commodityCode") String commodityCode, @Param("count") Integer count);

    /**
     * 根据id查询
     * @param id id
     * @Return:
     */
    StorageDO loadById(Integer id);

}
复制代码
2.7.9 StorageService
package com.ph.storage.service;

import com.ph.storage.dto.CommodityDTO;
import com.ph.storage.model.StorageDO;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ph.web.common.response.WrapperResponse;

/**
 * <p>
 *  服务类
 * </p>
 *
 * @author swallow
 * @since 2021-07-08
 */
public interface StorageService extends IService<StorageDO> {

    WrapperResponse<Object> decreaseStorage(CommodityDTO commodityDTO);

    WrapperResponse<StorageDO> loadObject(CommodityDTO commodityDTO);

    WrapperResponse<StorageDO> loadObjectAndGlobalLock(CommodityDTO commodityDTO);

    WrapperResponse<StorageDO> loadObjectAndGlobalTransactional(CommodityDTO commodityDTO);

    WrapperResponse<StorageDO> loadObjectForUpdate(CommodityDTO commodityDTO);

    WrapperResponse<StorageDO> loadObjectAndGlobalLockForUpdate(CommodityDTO commodityDTO);

    WrapperResponse<StorageDO> loadObjectAndGlobalTransactionalForUpdate(CommodityDTO commodityDTO);

}
复制代码
2.7.10 StorageServiceImpl
package com.ph.storage.service.impl;

import com.ph.storage.dto.CommodityDTO;
import com.ph.storage.model.StorageDO;
import com.ph.storage.mapper.StorageMapper;
import com.ph.storage.service.StorageService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ph.web.common.response.WrapperResponse;
import io.seata.spring.annotation.GlobalLock;
import io.seata.spring.annotation.GlobalTransactional;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author swallow
 * @since 2021-07-08
 */
@Service
public class StorageServiceImpl extends ServiceImpl<StorageMapper, StorageDO> implements StorageService {

    @Override
//    @GlobalTransactional
    @Transactional
    public WrapperResponse<Object> decreaseStorage(CommodityDTO commodityDTO) {
        int storage = baseMapper.decreaseStorage(commodityDTO.getCommodityCode(), commodityDTO.getCount());
        if (storage > 0) {
            return WrapperResponse.buildSuccess();
        }
        return WrapperResponse.buildError("扣减失败");
    }

    public WrapperResponse<StorageDO> loadObjectForUpdate(CommodityDTO commodityDTO) {
        return WrapperResponse.buildSuccess(baseMapper.loadById(commodityDTO.getId()));
    }

    @GlobalLock
    @Transactional
    public WrapperResponse<StorageDO> loadObjectAndGlobalLockForUpdate(CommodityDTO commodityDTO) {
        return WrapperResponse.buildSuccess(baseMapper.loadById(commodityDTO.getId()));
    }

    @GlobalTransactional
    public WrapperResponse<StorageDO> loadObjectAndGlobalTransactionalForUpdate(CommodityDTO commodityDTO) {
        return WrapperResponse.buildSuccess(baseMapper.loadById(commodityDTO.getId()));
    }

    public WrapperResponse<StorageDO> loadObject(CommodityDTO commodityDTO) {
        return WrapperResponse.buildSuccess(baseMapper.selectById(commodityDTO.getId()));
    }

    @GlobalLock
    @Transactional(rollbackFor = {Throwable.class})
    public WrapperResponse<StorageDO> loadObjectAndGlobalLock(CommodityDTO commodityDTO) {
        return WrapperResponse.buildSuccess(baseMapper.selectById(commodityDTO.getId()));
    }

    @GlobalTransactional
    public WrapperResponse<StorageDO> loadObjectAndGlobalTransactional(CommodityDTO commodityDTO) {
        return WrapperResponse.buildSuccess(baseMapper.selectById(commodityDTO.getId()));
    }

}
复制代码
2.7.11 StorageController
package com.ph.storage.controller;

import com.ph.storage.dto.CommodityDTO;
import com.ph.storage.model.StorageDO;
import com.ph.storage.service.StorageService;
import com.ph.web.common.response.WrapperResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;

/**
 * <p>
 *  前端控制器
 * </p>
 *
 * @author swallow
 * @since 2021-07-08
 */
@RestController
@RequestMapping("/api/storage")
@Slf4j
@RequiredArgsConstructor
public class StorageController {

    private final StorageService storageService;

    @PostMapping(value = "/dec_storage")
    public WrapperResponse<Object> decreaseStorage(@RequestBody CommodityDTO commodityDTO) {
        log.info("请求扣减库存:{}", commodityDTO.toString());
        return storageService.decreaseStorage(commodityDTO);
    }

    @GetMapping(value = "/load_object")
    public WrapperResponse<StorageDO> loadObject(CommodityDTO commodityDTO) {
        return storageService.loadObject(commodityDTO);
    }

    @GetMapping(value = "/load_object_globalLock")
    public WrapperResponse<StorageDO> loadObjectAndGlobalLock(CommodityDTO commodityDTO) {
        return storageService.loadObjectAndGlobalLock(commodityDTO);
    }

    @GetMapping(value = "/load_object_transactional")
    public WrapperResponse<StorageDO> loadObjectAndGlobalTransactional(CommodityDTO commodityDTO) {
        return storageService.loadObjectAndGlobalTransactional(commodityDTO);
    }

    @GetMapping(value = "/load_object_for_update")
    public WrapperResponse<StorageDO> loadObjectForUpdate(CommodityDTO commodityDTO) {
        return storageService.loadObjectForUpdate(commodityDTO);
    }

    @GetMapping(value = "/load_object_globalLock_for_update")
    public WrapperResponse<StorageDO> loadObjectAndGlobalLockForUpdate(CommodityDTO commodityDTO) {
        return storageService.loadObjectAndGlobalLockForUpdate(commodityDTO);
    }

    @GetMapping(value = "/load_object_transactional_for_update")
    public WrapperResponse<StorageDO> loadObjectAndGlobalTransactionalForUpdate(CommodityDTO commodityDTO) {
        return storageService.loadObjectAndGlobalTransactionalForUpdate(commodityDTO);
    }

}
复制代码
2.8 simple-business
2.8.1 pom
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>ph-simple</artifactId>
        <groupId>com.ph.share</groupId>
        <version>1.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>simple-business</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>com.ph.share</groupId>
            <artifactId>simple-storage-api</artifactId>
            <version>1.0</version>
        </dependency>
        <dependency>
            <groupId>com.ph.share</groupId>
            <artifactId>simple-web-common</artifactId>
            <version>1.0</version>
            <exclusions>
                <exclusion>
                    <groupId>mysql</groupId>
                    <artifactId>mysql-connector-java</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>com.baomidou</groupId>
                    <artifactId>mybatis-plus-boot-starter</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>
复制代码
2.8.2 application.yml
# simple系列都是90开头
server:
  port: 9000
# swagger配置
swagger:
  api-domain: https://business.simple.com
  doc: false
  enabled: true
  description: ${swagger.title}
  base-package: com.ph.business
  title: 入口api
  version: 1.0


spring:
  application:
    name: ph-simple-business
  # 此处文件上传限制
  servlet:
    multipart:
      max-file-size: 20MB
      max-request-size: 20MB
  cloud:
    #注册中心地址
    nacos:
      discovery:
        server-addr: ph.base.cn:8848
feign:
  client:
    config:
      default:
        connect-timeout: 5000
        read-timeout: 20000


#设置日志级别,ERROR/WARN/INFO/DEBUG,默认是INFO以上才显示
logging:
  level:
    root: INFO
seata:
  # 首先用seata.applicationId 没有则用spring.cloud.alibaba.seata.applicationId 如果还没有就用 spring.application.name # 见SeataAutoConfiguration.globalTransactionScanner
  application-id: ph-simple-business
  # 首先用seata.txServiceGroup 如果没有则用 spring.cloud.alibaba.seata.txServiceGroup 如果还没有就用 applicationId + DEFAULT_SPRING_CLOUD_SERVICE_GROUP_POSTFIX(-seata-service-group) 见SeataAutoConfiguration.globalTransactionScanner
  tx-service-group: my_test_tx_group
  service:
    vgroup-mapping:
      my_test_tx_group: default
    grouplist:
#      default: ph.base.cn:8091
      default: localhost:8091
  # 关闭自动代理 继承了 mybatis-plus 且使用的是 seata-spring-boot-starter 使用下面方法关闭  问题地址见 http://seata.io/zh-cn/docs/overview/faq.html Q: 25. 使用mybatis-plus 动态数据源组件后undolog无法删除 ?
  enable-auto-data-source-proxy: false
  registry:
    type: nacos
    nacos:
      server-addr: ph.base.cn:8848
  config:
    type: nacos
    nacos:
      server-addr: ph.base.cn:8848
复制代码
2.8.3 BusinessApplication

创建包 com.ph.business

package com.ph.business;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;

@SpringBootApplication(scanBasePackages = {"com.ph"})
@EnableDiscoveryClient
@EnableFeignClients(basePackages = {"com.ph"})
public class BusinessApplication {

    public static void main(String[] args) {
        SpringApplication.run(BusinessApplication.class, args);
    }

}
复制代码
2.8.4 BusinessDTO

创建包com.ph.business.dto

package com.ph.business.dto;

import lombok.Data;

import java.io.Serializable;
import java.math.BigDecimal;

@Data
public class BusinessDTO implements Serializable {

    private static final long serialVersionUID = 1L;

    private String userId;

    private String commodityCode;

    private String name;

    private Integer count;

    private BigDecimal amount;

}
复制代码
2.8.5 BusinessService

创建 com.ph.business.service

package com.ph.business.service;

import com.ph.business.dto.BusinessDTO;
import com.ph.web.common.response.WrapperResponse;

public interface BusinessService {

    /**
     * 入库处理类
     */
    WrapperResponse<Object> handleBusiness(BusinessDTO businessDTO) throws Exception;
}
复制代码
2.8.6 BusinessServiceImpl

创建 com.ph.business.service.impl

package com.ph.business.service.impl;

import com.ph.business.dto.BusinessDTO;
import com.ph.business.service.BusinessService;
import com.ph.storage.dto.CommodityDTO;
import com.ph.storage.service.StorageFeignService;
import com.ph.web.common.response.WrapperResponse;
import io.seata.core.context.RootContext;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
@Slf4j
public class BusinessServiceImpl implements BusinessService {

    @Autowired
    private StorageFeignService storageService;

    @Override
    @GlobalTransactional(timeoutMills = 300000)
    public WrapperResponse<Object> handleBusiness(BusinessDTO businessDTO) throws Exception {
        log.info("开启全局事务,XID = " + RootContext.getXID());
        //1、扣减库存
        CommodityDTO commodityDTO = new CommodityDTO()
                .setCommodityCode(businessDTO.getCommodityCode())
                .setCount(businessDTO.getCount());
        WrapperResponse<Object> storageResponse = storageService.decreaseStorage(commodityDTO);

        System.out.println(1 / 0);

        if (storageResponse.getCode() != 0) {
            throw new Exception("创建订单失败");
        }
        return WrapperResponse.buildSuccess();
    }
}
复制代码
2.8.7 BusinessController

创建包 com.ph.business.controller

package com.ph.business.controller;

import com.ph.business.dto.BusinessDTO;
import com.ph.business.service.BusinessService;
import com.ph.web.common.response.WrapperResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api/business/")
@Slf4j
@RequiredArgsConstructor
public class BusinessController {

    private final BusinessService businessService;

    @PostMapping(value = "/buy")
    public WrapperResponse<Object> handleBusiness(@RequestBody BusinessDTO businessDTO) throws Exception{
        log.info("请求参数:{}",businessDTO.toString());
        return businessService.handleBusiness(businessDTO);
    }


}
复制代码

3. mysql安装

使用docker安装 简单高效

3.1 单机启动
docker run --name s-mysql -e MYSQL_ROOT_PASSWORD=swallow -d -p 3306:3306 -v /root/mysql/data:/var/lib/mysql mysql:5.7
复制代码

3.2 集群搭建

3.2.1 主从搭建

3.2.2 mycat 搭建mysql集群

3.2.3 新建数据库
--  新建3个数据库
-- simple_storage
CREATE TABLE `t_storage` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `commodity_code` varchar(255) DEFAULT NULL,
  `name` varchar(255) DEFAULT NULL,
  `count` int(11) DEFAULT '0',
  PRIMARY KEY (`id`),
  UNIQUE KEY `commodity_code` (`commodity_code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

INSERT INTO `simple_storage`.`t_storage`(`id`, `commodity_code`, `name`, `count`) VALUES (1, 'C201901140001', '水杯', 1000);

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 DEFAULT CHARSET=utf8;


-- simple_nacos
/******************************************/
/*   数据库全名 = nacos_config   */
/*   表名称 = config_info   */
/******************************************/
CREATE TABLE `config_info` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
  `data_id` varchar(255) NOT NULL COMMENT 'data_id',
  `group_id` varchar(255) DEFAULT NULL,
  `content` longtext NOT NULL COMMENT 'content',
  `md5` varchar(32) DEFAULT NULL COMMENT 'md5',
  `gmt_create` datetime NOT NULL DEFAULT '2010-05-05 00:00:00' COMMENT '创建时间',
  `gmt_modified` datetime NOT NULL DEFAULT '2010-05-05 00:00:00' COMMENT '修改时间',
  `src_user` text COMMENT 'source user',
  `src_ip` varchar(20) DEFAULT NULL COMMENT 'source ip',
  `app_name` varchar(128) DEFAULT NULL,
  `tenant_id` varchar(128) DEFAULT '' COMMENT '租户字段',
  `c_desc` varchar(256) DEFAULT NULL,
  `c_use` varchar(64) DEFAULT NULL,
  `effect` varchar(64) DEFAULT NULL,
  `type` varchar(64) DEFAULT NULL,
  `c_schema` text,
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_configinfo_datagrouptenant` (`data_id`,`group_id`,`tenant_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='config_info';

/******************************************/
/*   数据库全名 = nacos_config   */
/*   表名称 = config_info_aggr   */
/******************************************/
CREATE TABLE `config_info_aggr` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
  `data_id` varchar(255) NOT NULL COMMENT 'data_id',
  `group_id` varchar(255) NOT NULL COMMENT 'group_id',
  `datum_id` varchar(255) NOT NULL COMMENT 'datum_id',
  `content` longtext NOT NULL COMMENT '内容',
  `gmt_modified` datetime NOT NULL COMMENT '修改时间',
  `app_name` varchar(128) DEFAULT NULL,
  `tenant_id` varchar(128) DEFAULT '' COMMENT '租户字段',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_configinfoaggr_datagrouptenantdatum` (`data_id`,`group_id`,`tenant_id`,`datum_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='增加租户字段';


/******************************************/
/*   数据库全名 = nacos_config   */
/*   表名称 = config_info_beta   */
/******************************************/
CREATE TABLE `config_info_beta` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
  `data_id` varchar(255) NOT NULL COMMENT 'data_id',
  `group_id` varchar(128) NOT NULL COMMENT 'group_id',
  `app_name` varchar(128) DEFAULT NULL COMMENT 'app_name',
  `content` longtext NOT NULL COMMENT 'content',
  `beta_ips` varchar(1024) DEFAULT NULL COMMENT 'betaIps',
  `md5` varchar(32) DEFAULT NULL COMMENT 'md5',
  `gmt_create` datetime NOT NULL DEFAULT '2010-05-05 00:00:00' COMMENT '创建时间',
  `gmt_modified` datetime NOT NULL DEFAULT '2010-05-05 00:00:00' COMMENT '修改时间',
  `src_user` text COMMENT 'source user',
  `src_ip` varchar(20) DEFAULT NULL COMMENT 'source ip',
  `tenant_id` varchar(128) DEFAULT '' COMMENT '租户字段',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_configinfobeta_datagrouptenant` (`data_id`,`group_id`,`tenant_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='config_info_beta';

/******************************************/
/*   数据库全名 = nacos_config   */
/*   表名称 = config_info_tag   */
/******************************************/
CREATE TABLE `config_info_tag` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
  `data_id` varchar(255) NOT NULL COMMENT 'data_id',
  `group_id` varchar(128) NOT NULL COMMENT 'group_id',
  `tenant_id` varchar(128) DEFAULT '' COMMENT 'tenant_id',
  `tag_id` varchar(128) NOT NULL COMMENT 'tag_id',
  `app_name` varchar(128) DEFAULT NULL COMMENT 'app_name',
  `content` longtext NOT NULL COMMENT 'content',
  `md5` varchar(32) DEFAULT NULL COMMENT 'md5',
  `gmt_create` datetime NOT NULL DEFAULT '2010-05-05 00:00:00' COMMENT '创建时间',
  `gmt_modified` datetime NOT NULL DEFAULT '2010-05-05 00:00:00' COMMENT '修改时间',
  `src_user` text COMMENT 'source user',
  `src_ip` varchar(20) DEFAULT NULL COMMENT 'source ip',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_configinfotag_datagrouptenanttag` (`data_id`,`group_id`,`tenant_id`,`tag_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='config_info_tag';

/******************************************/
/*   数据库全名 = nacos_config   */
/*   表名称 = config_tags_relation   */
/******************************************/
CREATE TABLE `config_tags_relation` (
  `id` bigint(20) NOT NULL COMMENT 'id',
  `tag_name` varchar(128) NOT NULL COMMENT 'tag_name',
  `tag_type` varchar(64) DEFAULT NULL COMMENT 'tag_type',
  `data_id` varchar(255) NOT NULL COMMENT 'data_id',
  `group_id` varchar(128) NOT NULL COMMENT 'group_id',
  `tenant_id` varchar(128) DEFAULT '' COMMENT 'tenant_id',
  `nid` bigint(20) NOT NULL AUTO_INCREMENT,
  PRIMARY KEY (`nid`),
  UNIQUE KEY `uk_configtagrelation_configidtag` (`id`,`tag_name`,`tag_type`),
  KEY `idx_tenant_id` (`tenant_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='config_tag_relation';

/******************************************/
/*   数据库全名 = nacos_config   */
/*   表名称 = group_capacity   */
/******************************************/
CREATE TABLE `group_capacity` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
  `group_id` varchar(128) NOT NULL DEFAULT '' COMMENT 'Group ID,空字符表示整个集群',
  `quota` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '配额,0表示使用默认值',
  `usage` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '使用量',
  `max_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '单个配置大小上限,单位为字节,0表示使用默认值',
  `max_aggr_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '聚合子配置最大个数,,0表示使用默认值',
  `max_aggr_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '单个聚合数据的子配置大小上限,单位为字节,0表示使用默认值',
  `max_history_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '最大变更历史数量',
  `gmt_create` datetime NOT NULL DEFAULT '2010-05-05 00:00:00' COMMENT '创建时间',
  `gmt_modified` datetime NOT NULL DEFAULT '2010-05-05 00:00:00' COMMENT '修改时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_group_id` (`group_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='集群、各Group容量信息表';

/******************************************/
/*   数据库全名 = nacos_config   */
/*   表名称 = his_config_info   */
/******************************************/
CREATE TABLE `his_config_info` (
  `id` bigint(64) unsigned NOT NULL,
  `nid` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `data_id` varchar(255) NOT NULL,
  `group_id` varchar(128) NOT NULL,
  `app_name` varchar(128) DEFAULT NULL COMMENT 'app_name',
  `content` longtext NOT NULL,
  `md5` varchar(32) DEFAULT NULL,
  `gmt_create` datetime NOT NULL DEFAULT '2010-05-05 00:00:00',
  `gmt_modified` datetime NOT NULL DEFAULT '2010-05-05 00:00:00',
  `src_user` text,
  `src_ip` varchar(20) DEFAULT NULL,
  `op_type` char(10) DEFAULT NULL,
  `tenant_id` varchar(128) DEFAULT '' COMMENT '租户字段',
  PRIMARY KEY (`nid`),
  KEY `idx_gmt_create` (`gmt_create`),
  KEY `idx_gmt_modified` (`gmt_modified`),
  KEY `idx_did` (`data_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='多租户改造';


/******************************************/
/*   数据库全名 = nacos_config   */
/*   表名称 = tenant_capacity   */
/******************************************/
CREATE TABLE `tenant_capacity` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
  `tenant_id` varchar(128) NOT NULL DEFAULT '' COMMENT 'Tenant ID',
  `quota` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '配额,0表示使用默认值',
  `usage` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '使用量',
  `max_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '单个配置大小上限,单位为字节,0表示使用默认值',
  `max_aggr_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '聚合子配置最大个数',
  `max_aggr_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '单个聚合数据的子配置大小上限,单位为字节,0表示使用默认值',
  `max_history_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '最大变更历史数量',
  `gmt_create` datetime NOT NULL DEFAULT '2010-05-05 00:00:00' COMMENT '创建时间',
  `gmt_modified` datetime NOT NULL DEFAULT '2010-05-05 00:00:00' COMMENT '修改时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_tenant_id` (`tenant_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='租户容量信息表';


CREATE TABLE `tenant_info` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
  `kp` varchar(128) NOT NULL COMMENT 'kp',
  `tenant_id` varchar(128) default '' COMMENT 'tenant_id',
  `tenant_name` varchar(128) default '' COMMENT 'tenant_name',
  `tenant_desc` varchar(256) DEFAULT NULL COMMENT 'tenant_desc',
  `create_source` varchar(32) DEFAULT NULL COMMENT 'create_source',
  `gmt_create` bigint(20) NOT NULL COMMENT '创建时间',
  `gmt_modified` bigint(20) NOT NULL COMMENT '修改时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_tenant_info_kptenantid` (`kp`,`tenant_id`),
  KEY `idx_tenant_id` (`tenant_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='tenant_info';

CREATE TABLE users (
	username varchar(50) NOT NULL PRIMARY KEY,
	password varchar(500) NOT NULL,
	enabled boolean NOT NULL
);

CREATE TABLE roles (
	username varchar(50) NOT NULL,
	role varchar(50) NOT NULL
);
CREATE TABLE `permissions` (
    `role` varchar(50) NOT NULL,
    `resource` varchar(255) NOT NULL,
    `action` varchar(8) NOT NULL,
    UNIQUE INDEX `uk_role_permission` (`role`,`resource`,`action`) USING BTREE
);
INSERT INTO users (username, password, enabled) VALUES ('nacos', '$2a$10$EuWPZHzz32dJN7jexM34MOeYirDdFAZm2kuWj7VEOJhhZkDrxfvUu', TRUE);

INSERT INTO roles (username, role) VALUES ('nacos', 'ROLE_ADMIN');



-- simple_seata
CREATE TABLE `branch_table` (
  `branch_id` bigint(20) NOT NULL,
  `xid` varchar(128) NOT NULL,
  `transaction_id` bigint(20) DEFAULT NULL,
  `resource_group_id` varchar(32) DEFAULT NULL,
  `resource_id` varchar(256) DEFAULT NULL,
  `lock_key` varchar(128) DEFAULT NULL,
  `branch_type` varchar(8) DEFAULT NULL,
  `status` tinyint(4) DEFAULT NULL,
  `client_id` varchar(64) DEFAULT NULL,
  `application_data` varchar(2000) DEFAULT NULL,
  `gmt_create` datetime DEFAULT NULL,
  `gmt_modified` datetime DEFAULT NULL,
  PRIMARY KEY (`branch_id`),
  KEY `idx_xid` (`xid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `global_table` (
  `xid` varchar(128) NOT NULL,
  `transaction_id` bigint(20) DEFAULT NULL,
  `status` tinyint(4) NOT NULL,
  `application_id` varchar(32) DEFAULT NULL,
  `transaction_service_group` varchar(32) DEFAULT NULL,
  `transaction_name` varchar(128) DEFAULT NULL,
  `timeout` int(11) DEFAULT NULL,
  `begin_time` bigint(20) DEFAULT NULL,
  `application_data` varchar(2000) DEFAULT NULL,
  `gmt_create` datetime DEFAULT NULL,
  `gmt_modified` datetime DEFAULT NULL,
  PRIMARY KEY (`xid`),
  KEY `idx_gmt_modified_status` (`gmt_modified`,`status`),
  KEY `idx_transaction_id` (`transaction_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `lock_table` (
  `row_key` varchar(128) NOT NULL,
  `xid` varchar(96) DEFAULT NULL,
  `transaction_id` mediumtext,
  `branch_id` mediumtext,
  `resource_id` varchar(256) DEFAULT NULL,
  `table_name` varchar(32) DEFAULT NULL,
  `pk` varchar(36) DEFAULT NULL,
  `gmt_create` datetime DEFAULT NULL,
  `gmt_modified` datetime DEFAULT NULL,
  PRIMARY KEY (`row_key`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
复制代码

4. 运行nacos

使用docker安装 简单高效

4.1 单机启动
#### 对着自己的需求更改
docker run --name=s-nacos -p 8848:8848 -e SPRING_DATASOURCE_PLATFORM=mysql -e MYSQL_SERVICE_HOST=192.168.56.103 -e MYSQL_SERVICE_PORT=3306 -e MYSQL_SERVICE_USER=root -e MYSQL_SERVICE_PASSWORD=swallow -e MYSQL_SERVICE_DB_NAME=learn_nacos -e MODE=standalone -d nacos/nacos-server
复制代码

5. 运行seata

5.1 下载seata包

此处使用的版本是1.3.0

直接去官网下载:seata.io/zh-cn/blog/…

上传到自己的服务器并解压

# 上传到指定目录 目录自己自定义
unzip seata-server-1.3.0.zip
cd seata
# 解析
├── bin
│   ├── seata-server.bat
│   └── seata-server.sh
├── conf
│   ├── file.conf  # 主要文件1
│   ├── file.conf.example
│   ├── logback.xml
│   ├── META-INF
│   │   └── services
│   │       ├── io.seata.core.rpc.RegisterCheckAuthHandler
│   │       ├── io.seata.core.store.db.DataSourceProvider
│   │       ├── io.seata.server.coordinator.AbstractCore
│   │       ├── io.seata.server.lock.LockManager
│   │       └── io.seata.server.session.SessionManager
│   ├── README.md
│   ├── README-zh.md
│   └── registry.conf # 主要文件2  注册地址 如果用了所有都用了nacos的话 就不需要file.conf了 此处用的都是nacos
复制代码
5.2 修改registry.conf文件
registry {
  # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
  type = "nacos"

  nacos {
    application = "seata-server"
    serverAddr = "192.168.56.190"
    group = "SEATA_GROUP"
    namespace = ""
    cluster = "default"
    username = "nacos"
    password = "nacos"
  }
}

config {
  # file、nacos 、apollo、zk、consul、etcd3
  type = "nacos"

  nacos {
    serverAddr = "192.168.56.190"
    namespace = ""
    group = "SEATA_GROUP"
    username = "nacos"
    password = "nacos"
  }
}
复制代码
5.3 下载源码找到script/config-center/config.txt文件

记得把# 开头的注释全都去掉哦

transport.type=TCP
transport.server=NIO
transport.heartbeat=true
transport.enableClientBatchSendRequest=false
transport.threadFactory.bossThreadPrefix=NettyBoss
transport.threadFactory.workerThreadPrefix=NettyServerNIOWorker
transport.threadFactory.serverExecutorThreadPrefix=NettyServerBizHandler
transport.threadFactory.shareBossWorker=false
transport.threadFactory.clientSelectorThreadPrefix=NettyClientSelector
transport.threadFactory.clientSelectorThreadSize=1
transport.threadFactory.clientWorkerThreadPrefix=NettyClientWorkerThread
transport.threadFactory.bossThreadSize=1
transport.threadFactory.workerThreadSize=default
transport.shutdown.wait=3

# service 配置 此处需要跟client配置一致
service.vgroupMapping.my_test_tx_group=default
# service 配置 此处填写的是当前seata运行的节点  多个节点用逗号隔开
service.default.grouplist=192.168.56.190:8091

service.enableDegrade=false
service.disableGlobalTransaction=false
client.rm.asyncCommitBufferLimit=10000
client.rm.lock.retryInterval=10
client.rm.lock.retryTimes=30
client.rm.lock.retryPolicyBranchRollbackOnConflict=true
client.rm.reportRetryCount=5
client.rm.tableMetaCheckEnable=false
client.rm.sqlParserType=druid
client.rm.reportSuccessEnable=false
client.rm.sagaBranchRegisterEnable=false
client.tm.commitRetryCount=5
client.tm.rollbackRetryCount=5
client.tm.degradeCheck=false
client.tm.degradeCheckAllowTimes=10
client.tm.degradeCheckPeriod=2000

# mode修改成db
store.mode=db
store.file.dir=file_store/data
store.file.maxBranchSessionSize=16384
store.file.maxGlobalSessionSize=512
store.file.fileWriteBufferCacheSize=16384
store.file.flushDiskMode=async
store.file.sessionReloadReadSize=100

# 此处修改成db的配置
store.db.datasource=druid
store.db.dbType=mysql
store.db.driverClassName=com.mysql.jdbc.Driver
store.db.url=jdbc:mysql://192.168.56.190:3306/simple_seata?useUnicode=true
store.db.user=root
store.db.password=swallow
store.db.minConn=5
store.db.maxConn=30
store.db.globalTable=global_table
store.db.branchTable=branch_table
store.db.queryLimit=100
store.db.lockTable=lock_table
store.db.maxWait=5000
store.redis.host=127.0.0.1
store.redis.port=6379
store.redis.maxConn=10
store.redis.minConn=1
store.redis.database=0
store.redis.password=null
store.redis.queryLimit=100
server.recovery.committingRetryPeriod=1000
server.recovery.asynCommittingRetryPeriod=1000
server.recovery.rollbackingRetryPeriod=1000
server.recovery.timeoutRetryPeriod=1000
server.maxCommitRetryTimeout=-1
server.maxRollbackRetryTimeout=-1
server.rollbackRetryTimeoutUnlockEnable=false
client.undo.dataValidation=true
client.undo.logSerialization=jackson
client.undo.onlyCareUpdateColumns=true
server.undo.logSaveDays=7
server.undo.logDeletePeriod=86400000
client.undo.logTable=undo_log
client.log.exceptionRate=100
transport.serialization=seata
transport.compressor=none
metrics.enabled=false
metrics.registryType=compact
metrics.exporterList=prometheus
metrics.exporterPrometheusPort=9898
复制代码
5.4 运行python脚本
# python原文
import http.client
import sys

if len(sys.argv) != 2:
    print ('python nacos-config.py nacosAddr')
    exit()

headers = {
    'content-type': "application/x-www-form-urlencoded"
}

hasError = False
# 注意目录
for line in open('./config.txt'):
    pair = line.split('=')
    if len(pair) < 2:
        continue
    print (line),
    url_prefix = sys.argv[1]
    conn = http.client.HTTPConnection(url_prefix)
    if len(sys.argv) == 3:
        namespace=sys.argv[2]
        url_postfix = '/nacos/v1/cs/configs?dataId={0}&group=SEATA_GROUP&content={1}&tenant={2}'.format(str(pair[0]),str(line[line.index('=')+1:]).strip(),namespace)
    else:
        url_postfix = '/nacos/v1/cs/configs?dataId={}&group=SEATA_GROUP&content={}'.format(str(pair[0]),str(line[line.index('=')+1:])).strip()
    conn.request("POST", url_postfix, headers=headers)
    res = conn.getresponse()
    data = res.read()
    if data.decode("utf-8") != "true":
        hasError = True
if hasError:
    print ("init nacos config fail.")
else:
    print ("init nacos config finished, please start seata-server.")
复制代码
5.5 执行
python nacos-config.py 192.168.56.190
# 看到 最后的 init nacos config finished, please start seata-server. 说明正确
复制代码
5.6 在nacos查看是否ok

image-20210712112739868

5.7 启动seata
## 跳转到seata的bin目录 记得装一下jdk哈
cd ~/seata/bin/
sh seata.sh
# 如果 内存不够 直接vim seata.sh 修改启动的参数就行 直接搜 -X 就能看到了 如果启动报错啥的 报错信息都比较明显 什么节点问题呀(单机基本不存在)什么路径问题呀(非nginx配置 基本不存在)其他问题基本没见过 可能我比较帅!!!! 所以八二哥不好意思看我
复制代码

6. 测试

启动storage和business服务 记得修改application.yml 的相关配置,启动报错就遇到没找到对应的服务啥的,然后就是没有注册上,这些都是因为nacos和seata配置出问题导致的(集群环境可能还有端口问题,本人此次将所有的防火墙都关闭了,所以没有遇到太恶心的问题)

先打断点

image-20210712142128128

本人此次使用的是postman测试,其实可以用swagger测试的

image-20210712142035345

点击Send之后进入debug环境,此时看相关数据表

simple_seata的3张表

image-20210712142425339

simple_storage的2张表

image-20210712142628873

释放断点,发现simple_seata的3张事务表全部清空了,simple_storage的undo_log数据表也清空了(如果开启了自动代理,那么可能就会留有一些数据此处解决方案官方已经提供了 我的配置中也有相关链接 问题地址见 seata.io/zh-cn/docs/… Q: 25. 使用mybatis-plus 动态数据源组件后undolog无法删除 ?)

7 思考

  • 在处理分布式事务的时候,一个修改接口可能会被很多服务调用(日常工作中其实很少的呀,因为接口其实也是会根据业务或者用户来区分不通数据的,被调用多次的原因大部分是因为rpc服务重复调用导致的(可能很多小伙伴遇见不了,可以模拟呀,开多线程debug模式,然后两个服务之间调用,被调用者打断点,你就能看到这种场景了)),遇到这种场景,本文搭建的环境,就会出现脏数据,目前只能手动处理了(其实可以用定时任务去执行,但是目前处理逻辑比较复杂,性价比极低,而且还需要根据相关业务处理,这点是真的烦,本人没啥好想法,求指点);
  • 事务隔离的问题,关于上一个问题,其实也是事务隔离的问题,如果有相关的锁,普通项目都能接受,所以seata提供了 GloabLock+语句for update来处理,这个对于直接写sql的那群人比较友好,而且查询的时候加锁,如果请求链路过长,其他正常请求都会报错,seata也提供了两个参数来处理这个问题:seata.io/zh-cn/docs/… client.rm.lock.retryInterval client.rm.lock.retryTimes 前者 校验或占用全局锁重试间隔 ,后者校验或占用全局锁重试次数,可以直接看官方文档,但是目前测试时木有效果的,查看源码 发现LockProperties 压根没地方用 就初始化加载了下 然后加载的比对地方也没有,也不清楚在哪使用的,暂时不去碰了,有大佬知道可以分享一哈
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享