spring alibaba seata 分布式事务实践

1.启动seata-server

简介:本文采用docker部署方式,采用file的配置方式,也可以使用nacos 注册中心,这里为了讲解功能使用,就简化了注册中心环节。

docker-compose.yml

version: "3"
services:
  seata-server:
    image: seataio/seata-server
    hostname: seata-server
    container_name: seata-server
    ports:
      - "8091:8091"
    environment:
      - SEATA_PORT=8091
    volumes:
      - ./config:/root/seata-config

registry.conf

registry {
  type = "file"

  file {
    name = "file.conf"
  }
}

config {
  type = "file"

  file {
    name = "file.conf"
  }
}

file.conf

service {
  vgroup_mapping.tx_group = "default"
  default.grouplist = "127.0.0.1:8091"
  disableGlobalTransaction = false
}

store {
  mode = "file"

  file {
    dir = "sessionStore"
  }
}

tx_group 自定义服务组名,这里的client的group名一定要和server的group一致,不然分布式事务无法生效。

 启动成功

2.创建数据库

起另一台服务器

创建db_order 数据库,端口号3336

version: '3.1'
services:
  db_order:
    image: mysql
    container_name: db_order
    environment:
      MYSQL_ROOT_PASSWORD: 123456
    command:
      --default-authentication-plugin=mysql_native_password
      --character-set-server=utf8mb4
      --collation-server=utf8mb4_general_ci
      --explicit_defaults_for_timestamp=true
      --lower_case_table_names=true
    ports:
      - 3336:3306
    volumes:
      - ./data:/var/lib/mysql

创建db_order_item数据库,端口号3346

version: '3.1'
services:
  db_order_item:
    image: mysql
    container_name: db_order_item
    environment:
      MYSQL_ROOT_PASSWORD: 123456
    command:
      --default-authentication-plugin=mysql_native_password
      --character-set-server=utf8mb4
      --collation-server=utf8mb4_general_ci
      --explicit_defaults_for_timestamp=true
      --lower_case_table_names=true
    ports:
      - 3346:3306
    volumes:
      - ./data:/var/lib/mysql

3. 创建数据库表结构

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;

 创建表tb_order

CREATE TABLE tb_order (
id bigint NOT NULL AUTO_INCREMENT,
order_id bigint DEFAULT NULL,
user_id bigint DEFAULT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

创建表tb_order_item

CREATE TABLE tb_order_item (
id bigint NOT NULL AUTO_INCREMENT,
user_id bigint DEFAULT NULL,
order_id bigint DEFAULT NULL,
order_item_id bigint DEFAULT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

4.创建工程

完整的demo 已上传github 地址:https://github.com/liangzhe9/spring-alibaba-seata

 1.创建主工程pom

hello-seata

<?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>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.2.RELEASE</version>
        <relativePath/>
    </parent>

    <groupId>com.devin</groupId>
    <artifactId>hello-seata</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>pom</packaging>

    <modules>
        <module>dependencies</module>
        <module>commons</module>
        <module>business</module>
        <module>provider</module>
    </modules>

    <properties>
        <skipTests>true</skipTests>
        <java.version>1.8</java.version>
        <maven.compiler.source>${java.version}</maven.compiler.source>
        <maven.compiler.target>${java.version}</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>com.devin</groupId>
                <artifactId>dependencies</artifactId>
                <version>${project.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

</project>

2. 创建dependencies module

pom.xml

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.devin</groupId>
    <artifactId>dependencies</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>dependencies</name>
    <packaging>pom</packaging>

    <properties>
        <dubbo.version>2.7.4.1</dubbo.version>
        <spring-cloud.version>Greenwich.RELEASE</spring-cloud.version>
        <spring-cloud-alibaba.verion>2.1.1.RELEASE</spring-cloud-alibaba.verion>
        <spring-boot-mapper.version>2.1.5</spring-boot-mapper.version>
        <spring-boot-pagehelper.version>1.2.13</spring-boot-pagehelper.version>
        <alibaba-seata.version>1.0.0</alibaba-seata.version>
        <alibaba-spring-context-support.version>1.0.5</alibaba-spring-context-support.version>
        <okhttp3.version>4.2.2</okhttp3.version>
    </properties>


    <dependencyManagement>
        <dependencies>
            <!-- Spring Cloud -->
            <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.verion}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

            <!-- Apache Dubbo -->
            <dependency>
                <groupId>org.apache.dubbo</groupId>
                <artifactId>dubbo</artifactId>
                <version>${dubbo.version}</version>
            </dependency>
            <dependency>
                <groupId>org.apache.dubbo</groupId>
                <artifactId>dubbo-registry-nacos</artifactId>
                <version>${dubbo.version}</version>
            </dependency>
            <dependency>
                <groupId>org.apache.dubbo</groupId>
                <artifactId>dubbo-spring-boot-starter</artifactId>
                <version>${dubbo.version}</version>
            </dependency>
            <dependency>
                <groupId>org.apache.dubbo</groupId>
                <artifactId>dubbo-serialization-kryo</artifactId>
                <version>${dubbo.version}</version>
            </dependency>
            <dependency>
                <groupId>com.alibaba.spring</groupId>
                <artifactId>spring-context-support</artifactId>
                <version>${alibaba-spring-context-support.version}</version>
            </dependency>


            <!-- Alibaba Seata -->
            <dependency>
                <groupId>io.seata</groupId>
                <artifactId>seata-spring-boot-starter</artifactId>
                <version>${alibaba-seata.version}</version>
            </dependency>

            <!-- Others -->
            <dependency>
                <groupId>tk.mybatis</groupId>
                <artifactId>mapper-spring-boot-starter</artifactId>
                <version>${spring-boot-mapper.version}</version>
            </dependency>
            <dependency>
                <groupId>com.github.pagehelper</groupId>
                <artifactId>pagehelper-spring-boot-starter</artifactId>
                <version>${spring-boot-pagehelper.version}</version>
            </dependency>
            <dependency>
                <groupId>com.squareup.okhttp3</groupId>
                <artifactId>okhttp</artifactId>
                <version>${okhttp3.version}</version>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <repositories>
        <repository>
            <id>alimaven</id>
            <name>aliyun maven</name>
            <url>https://maven.aliyun.com/repository/public</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>

        <repository>
            <id>spring-milestone</id>
            <name>Spring Milestone</name>
            <url>https://repo.spring.io/milestone</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
        <repository>
            <id>spring-snapshot</id>
            <name>Spring Snapshot</name>
            <url>https://repo.spring.io/snapshot</url>
            <snapshots>
                <enabled>true</enabled>
            </snapshots>
        </repository>
    </repositories>

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

        <pluginRepository>
            <id>spring-milestone</id>
            <name>Spring Milestone</name>
            <url>https://repo.spring.io/milestone</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </pluginRepository>
        <pluginRepository>
            <id>spring-snapshot</id>
            <name>Spring Snapshot</name>
            <url>https://repo.spring.io/snapshot</url>
            <snapshots>
                <enabled>true</enabled>
            </snapshots>
        </pluginRepository>
    </pluginRepositories>
</project>

3. 创建common module

pom.xml

<?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>

    <parent>
        <groupId>com.devin</groupId>
        <artifactId>commons</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>

    <artifactId>commons-mapper</artifactId>
    <packaging>jar</packaging>

    <dependencies>
        <!-- Datasource -->
        <dependency>
            <groupId>com.zaxxer</groupId>
            <artifactId>HikariCP</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.apache.tomcat</groupId>
                    <artifactId>tomcat-jdbc</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>tk.mybatis</groupId>
            <artifactId>mapper-spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper-spring-boot-starter</artifactId>
        </dependency>

        <!-- Others -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    </dependencies>
</project>

创建MyMapper 继承tk mybatis的mapper

public interface MyMapper<T> extends Mapper<T>, MySqlMapper<T> {

}

4. 创建provider RPC dubbo提供者 module

<modules>
    <module>provider-order-api</module>
    <module>provider-order-item-api</module>
    <module>provider-transaction-api</module>
    <module>provider-order-service</module>
    <module>provider-order-item-service</module>
    <module>provider-transaction-service</module>
</modules>

provider-order-item-api 开放对外item相关interface

provider-order-item-service 处理item表数据,api的具体实现

provider-order-api 开放对外order相关interface

provider-order-service 处理order表数据,api的具体实现

provider-transaction-api 暴露对外dubbo api

provider-transaction-service 同时处理order、item两个业务service

package com.devin.spring.cloud.alibaba.provider.service.impl;

import com.devin.spring.cloud.alibaba.provider.domain.TbOrder;
import com.devin.spring.cloud.alibaba.provider.domain.TbOrderItem;
import com.devin.spring.cloud.alibaba.provider.service.api.ProviderTransactionService;
import com.devin.spring.cloud.alibaba.provider.service.api.TbOrderItemService;
import com.devin.spring.cloud.alibaba.provider.service.api.TbOrderService;
import io.seata.spring.annotation.GlobalTransactional;
import org.apache.dubbo.config.annotation.Reference;
import org.apache.dubbo.config.annotation.Service;

@Service(version = "1.0.0")
public class ProviderTransactionServiceImpl implements ProviderTransactionService {

    @Reference(version = "1.0.0")
    private TbOrderService tbOrderService;

    @Reference(version = "1.0.0")
    private TbOrderItemService TbOrderItemService;

    @Override
    @GlobalTransactional
    public void createOrder(TbOrder order, TbOrderItem item) {
        tbOrderService.insert(order);
        TbOrderItemService.insert(item);
        if (order.getUserId().equals(2L)) {
            throw new RuntimeException("seata has exception.");
        }
    }
}

这里简单处理了,当用户userId = 2L 的时候,直接抛出异常,触发事务,进行回滚

application.yml

spring:
  application:
    name: provider-transaction
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.6.162:8848
    sentinel:
      transport:
        enable: true
        port: 8720
        dashboard: localhost:8081
    alibaba:
      seata:
        tx-service-group: tx-group

dubbo:
  scan:
    base-packages: com.devin.spring.cloud.alibaba.provider.service
  protocol:
    name: dubbo
    port: -1
  provider:
    loadbalance: roundrobin
  registry:
    address: nacos://192.168.6.162:8848

5. 创建business controller web服务 module

pom.xml

<?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>

    <parent>
        <groupId>com.devin</groupId>
        <artifactId>business</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>

    <artifactId>business-transaction-service</artifactId>
    <packaging>jar</packaging>

    <dependencies>
        <!-- Spring Boot -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

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

        <!-- Spring Cloud -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>

        <!-- Apache Dubbo -->
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.nacos</groupId>
            <artifactId>nacos-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-registry-nacos</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.spring</groupId>
            <artifactId>spring-context-support</artifactId>
        </dependency>

        <!-- Projects -->
        <dependency>
            <groupId>com.devin</groupId>
            <artifactId>provider-transaction-api</artifactId>
            <version>${project.parent.version}</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <mainClass>com.devin.spring.cloud.alibaba.business.BusinessTransactionApplication</mainClass>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

application.yml

spring:
  application:
    name: business-transaction
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.6.162:8848
    sentinel:
      transport:
        port: 8722
        dashboard: localhost:8081

dubbo:
  scan:
    base-packages: com.devin.spring.cloud.alibaba.business.controller
  protocol:
    name: dubbo
    port: -1
  provider:
    loadbalance: roundrobin
  registry:
    address: nacos://192.168.6.162:8848

server:
  port: 12001

management:
  endpoints:
    web:
      exposure:
        include: "*"

controller

package com.devin.spring.cloud.alibaba.business.controller;


import com.devin.spring.cloud.alibaba.provider.domain.TbOrder;
import com.devin.spring.cloud.alibaba.provider.domain.TbOrderItem;
import com.devin.spring.cloud.alibaba.provider.service.api.ProviderTransactionService;
import org.apache.dubbo.config.annotation.Reference;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping(value = "transaction")
public class BusinessTransactionController {

    @Reference(version = "1.0.0")
    ProviderTransactionService providerTransactionService;

    @GetMapping(value = "create/order")
    public String createOrder() {
        TbOrder order = new TbOrder();
        order.setOrderId(1L);
        order.setUserId(2L);

        TbOrderItem orderItem = new TbOrderItem();
        orderItem.setUserId(2L);
        orderItem.setOrderId(1L);
        orderItem.setOrderItemId(1L);

        providerTransactionService.createOrder(order, orderItem);
        return "ok";
    }
}

运行结果

tb_order_item表

tb_order表

访问web项目http://localhost:12001/transaction/create/order

 

触发异常

seata-server log如下,Rollback global transaction successfuly

ProviderOrderApplication

ProviderItemApplication

回滚成功

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值