seata实战


eureka

启动eureka

搭建TC

下载 seata-server

http://seata.io/zh-cn/blog/download.html

修改配置。

修改registry.conf

  • 注册中心 eureka
  • 配置中心 file
registry {
  # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
  #修改1,注册到eureka里
  type = "eureka"

  nacos {
    serverAddr = "localhost"
    namespace = ""
    cluster = "default"
  }
  #修改2
  eureka {
    serviceUrl = "http://localhost:7900/eureka/"
    #application = "default"
	application = "seata-server"
    weight = "1"
  }
  redis {
    serverAddr = "localhost:6379"
    db = "0"
  }
  zk {
    cluster = "default"
    serverAddr = "127.0.0.1:2181"
    session.timeout = 6000
    connect.timeout = 2000
  }
  consul {
    cluster = "default"
    serverAddr = "127.0.0.1:8500"
  }
  etcd3 {
    cluster = "default"
    serverAddr = "http://localhost:2379"
  }
  sofa {
    serverAddr = "127.0.0.1:9603"
    application = "default"
    region = "DEFAULT_ZONE"
    datacenter = "DefaultDataCenter"
    cluster = "default"
    group = "SEATA_GROUP"
    addressWaitTime = "3000"
  }
  file {
    name = "file.conf"
  }
}

# 配置中心
# 如果type = "file",则从本地file.conf中获取配置参数
config {
  # file、nacos 、apollo、zk、consul、etcd3
  #修改3
  type = "file"

  nacos {
    serverAddr = "localhost"
    namespace = ""
  }
  consul {
    serverAddr = "127.0.0.1:8500"
  }
  apollo {
    app.id = "seata-server"
    apollo.meta = "http://192.168.1.204:8801"
  }
  zk {
    serverAddr = "127.0.0.1:2181"
    session.timeout = 6000
    connect.timeout = 2000
  }
  etcd3 {
    serverAddr = "http://localhost:2379"
  }
  #修改4
  file {
    name = "file.conf"
  }
}

修改file.conf

  • 服务信息
  • 存储

注意:jdbc的驱动

开发需要生产的db用户名和密码,可以配置到jar包的xml里,避免敏感信息泄漏

service {
  #transaction service group mapping
  #修改点1:my_tx_group:事务组,值相当于spring.application.name
  #vgroup_mapping.fbs_tx_group = "default"
  vgroup_mapping.my_tx_group = "seata-server"
  #only support when registry.type=file, please don't set multiple addresses
  
  #修改点2
  #default.grouplist = "127.0.0.1:8091"
  seata-server.grouplist = "127.0.0.1:8091"
  #disable seata
  disableGlobalTransaction = false
}

## transaction log store, only used in seata-server
store {
  ## store mode: file、db
  # 修改3
  mode = "db"

  ## file store property
  file {
    ## store location dir
    dir = "sessionStore"
  }

  ## database store property
  #修改4
  db {
    ## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp) etc.
	
    datasource = "druid"
    ## mysql/oracle/h2/oceanbase etc.
    db-type = "mysql"
    # 此处注意驱动名称,用的新旧版本哪个
    driver-class-name = "com.mysql.cj.jdbc.Driver"
    url = "jdbc:mysql://192.168.1.113:3306/seata-server?useUnicode=true&useSSL=false&characterEncoding=utf8&serverTimezone=Asia/Shanghai"
    user = "root"
    password = "123456"
  }
}

添加seata-server-db

自己建库,自己建表。

数据库名和 file.conf中一致。(seata-server)

分支事务表
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,
  `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(6) DEFAULT NULL,
  `gmt_modified` datetime(6) DEFAULT NULL,
  PRIMARY KEY (`branch_id`),
  KEY `idx_xid` (`xid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

全局事务表
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=utf8;


全局锁
CREATE TABLE `lock_table` (
  `row_key` varchar(128) NOT NULL,
  `xid` varchar(96) DEFAULT NULL,
  `transaction_id` bigint(20) DEFAULT NULL,
  `branch_id` bigint(20) NOT NULL,
  `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`),
  KEY `idx_branch_id` (`branch_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

脚本

https://github.com/seata/seata/tree/1.2.0/script

server(TC)

# github上的文件位置
seata/script/server/db/mysql.sql

client(TM/RM)

# github上的文件位置
seata/script/client/at/db/mysql.sql

AT模型

TM

项目名:seata-one

db名:seata-rm-one

业务:插入tbl_one

pom.xml


    com.alibaba.cloud
    spring-cloud-alibaba-seata
    2.2.0.RELEASE

注解

@GlobalTransactional(rollbackFor = Exception.class)

db.sql

CREATE TABLE `tbl_XXX` (
  `id` int(16) NOT NULL AUTO_INCREMENT,
  `name` varchar(16) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=31 DEFAULT CHARSET=utf8;

# 该表是规定好的
CREATE TABLE `undo_log` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `branch_id` bigint(20) NOT NULL,
  `xid` varchar(100) NOT NULL,
  `context` varchar(128) NOT NULL,
  `rollback_info` longblob NOT NULL,
  `log_status` int(11) NOT NULL,
  `log_created` datetime NOT NULL,
  `log_modified` datetime NOT NULL,
  `ext` varchar(100) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
) ENGINE=InnoDB AUTO_INCREMENT=41 DEFAULT CHARSET=utf8;

RM

项目名:seata-two

db名:seata-rm-two

业务:插入tbl_two

项目名:seata-three

db名:seata-rm-three

业务:插入tbl_three

下面两个配置,不配置也行,走默认8091。

registry.conf

file.conf

pom.xml


    com.alibaba.cloud
    spring-cloud-alibaba-seata
    2.2.0.RELEASE

db.sql

CREATE TABLE `tbl_XXX` (
  `id` int(16) NOT NULL AUTO_INCREMENT,
  `name` varchar(16) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=31 DEFAULT CHARSET=utf8;

# 该表是规定好的
CREATE TABLE `undo_log` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `branch_id` bigint(20) NOT NULL,
  `xid` varchar(100) NOT NULL,
  `context` varchar(128) NOT NULL,
  `rollback_info` longblob NOT NULL,
  `log_status` int(11) NOT NULL,
  `log_created` datetime NOT NULL,
  `log_modified` datetime NOT NULL,
  `ext` varchar(100) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
) ENGINE=InnoDB AUTO_INCREMENT=41 DEFAULT CHARSET=utf8;

代码演示

TM

pom.xml

<?xml version="1.0" encoding="UTF-8"?>

    4.0.0
    
        org.springframework.boot
        spring-boot-starter-parent
        2.3.2.RELEASE
         
    
    com.dandan
    seata-one
    0.0.1-SNAPSHOT
    seata-one
    Demo project for Spring Boot
    
        1.8
        Greenwich.SR2
    
    
        
            org.projectlombok
            lombok
            true
        

        
            org.springframework.boot
            spring-boot-starter-web
        
        
            org.springframework.cloud
            spring-cloud-starter-netflix-eureka-client
        

        
        
            org.mybatis.spring.boot
            mybatis-spring-boot-starter
            2.0.0
        

        
        
            mysql
            mysql-connector-java
        

        
        
            com.alibaba
            druid
            1.1.12
        

        
            com.alibaba.cloud
            spring-cloud-alibaba-seata
            2.2.0.RELEASE
        
        
            org.springframework.boot
            spring-boot-starter
        

        
            org.springframework.boot
            spring-boot-starter-test
            test
        
    

    
        
            
                org.springframework.cloud
                spring-cloud-dependencies
                Greenwich.SR2
                pom
                import
            
        
    

    
        
            
                org.springframework.boot
                spring-boot-maven-plugin
            
        
    


application.yml

server:
  port: 1001

spring:
  cloud:
    alibaba:
      seata:
        tx-service-group: my_tx_group
  application:
    name: one


  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://192.168.1.113:3306/seata-rm-one?characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
    username: root
    password: 123456
    dbcp2:
      initial-size: 5
      min-idle: 5
      max-total: 5
      max-wait-millis: 200
      validation-query: SELECT 1
      test-while-idle: true
      test-on-borrow: false
      test-on-return: false

mybatis:
  mapper-locations:
  - classpath:mapper/*.xml

eureka:
  client:
    service-url:
      defaultZone: http://localhost:7900/eureka/

#logging:
#  level:
#    root: debug

启动类

@SpringBootApplication
public class SeataOneApplication {

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

    @Bean
    @LoadBalanced
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

controller

@RestController
public class OneController {

    @Autowired
    RmOneService rmOneService;

    @GetMapping("/one")
    @GlobalTransactional(rollbackFor = Exception.class)
    public String one() throws InterruptedException {
        rmOneService.rm1();
//        TimeUnit.MINUTES.sleep(1);
        System.out.println(1/0);
        return "success";
    }    
}

RmOneService

@Service
public class RmOneService {
	
	@Autowired
	TblOneDao mapper;

	public String rm1() {
		TblOne o = new TblOne();
		o.setId(1);
		o.setName("rm1");
		mapper.insertSelective(o);

		rm2();
		rm3();
		
		return "";
	}	
	
	@Autowired
	private RestTemplate restTemplate;
	
	private void rm2() {
		restTemplate.getForEntity("http://two/rm2", null);
	}
	private void rm3() {
		restTemplate.getForEntity("http://three/rm3", null);
	}
}

RM1

pom.xml

同上

application.yml

同上

启动类

同上

controller

@RestController
public class TwoController {

    @Autowired
    private RmTwoService rmTwoService;

    @GetMapping("/rm2")
    public String two(){

        rmTwoService.rm2();
//        int i = 1/0;
        return "success";
    }
}

RmTwoService

@Service
public class RmTwoService {
	
	@Autowired
	TblTwoDao mapper;
	
	public String rm2() {
		TblTwo o = new TblTwo();
		o.setId(2);
		o.setName("rm2");
		mapper.insertSelective(o);
		
		return "";
	}
}

RM2

同RM1

TCC模型

try,confirm,cancel 中独立事务。通过业务回滚,此时不用undo.log表了。

tcc场景:1.混合场景。2。独立场景。

代码演示

pom.xml、application.yml、启动类配置同上

TM

RmOneInterface

package com.dandan.seataone.service;

import io.seata.rm.tcc.api.BusinessActionContext;
import io.seata.rm.tcc.api.LocalTCC;
import io.seata.rm.tcc.api.TwoPhaseBusinessAction;

@LocalTCC
public interface RmOneInterface {

    @TwoPhaseBusinessAction(name = "rm1TccAction" , commitMethod = "rm1Commit" ,rollbackMethod = "rm1Rollback")
    public String rm1(BusinessActionContext businessActionContext);

    public boolean rm1Commit(BusinessActionContext businessActionContext);

    public boolean rm1Rollback(BusinessActionContext businessActionContext);
}

controller

@Autowired
private RmOneInterface rmOneInterface;

@GetMapping("/one-tcc")
@GlobalTransactional(rollbackFor = Exception.class)
public String oneTcc() throws InterruptedException {
	rmOneInterface.rm1(null);
	return "success";
}

RmOneInterfaceImpl

package com.dandan.seataone.service;

import io.seata.rm.tcc.api.BusinessActionContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;

@Component
public class RmOneInterfaceImpl implements RmOneInterface {

    @Override
    @Transactional
    public String rm1(BusinessActionContext businessActionContext) {
        // 查询是事务记录表,xxxx
        System.out.println("rm1 try");

        rm2();
        rm3();
//        System.out.println(1/0);
        return null;
    }

    @Override
    @Transactional
    public boolean rm1Commit(BusinessActionContext businessActionContext) {
        System.out.println("rm1 confirm");
        return true;
    }

    @Override
    @Transactional
    public boolean rm1Rollback(BusinessActionContext businessActionContext) {
        System.out.println("rm1 rollback");
        return true;
    }


    @Autowired
    private RestTemplate restTemplate;

    private void rm2() {
        restTemplate.getForEntity("http://two/rm2-tcc", null);
    }

    private void rm3() {
        restTemplate.getForEntity("http://three/rm3-tcc", null);
    }
}

RM

RmTwoInterface

package com.dandan.seatatwo.service;

import io.seata.rm.tcc.api.BusinessActionContext;
import io.seata.rm.tcc.api.LocalTCC;
import io.seata.rm.tcc.api.TwoPhaseBusinessAction;

@LocalTCC
public interface RmTwoInterface {

    @TwoPhaseBusinessAction(name = "rm1TccAction" , commitMethod = "rm1Commit" ,rollbackMethod = "rm1Rollback")
    public String rm2(BusinessActionContext businessActionContext);

    public boolean rm1Commit(BusinessActionContext businessActionContext);

    public boolean rm1Rollback(BusinessActionContext businessActionContext);
}

TwoController

@Autowired
private RmTwoInterface rmTwoInterface;

@GetMapping("/rm2-tcc")
@GlobalTransactional(rollbackFor = Exception.class)
public String twoTcc(){
    rmTwoInterface.rm2(null);
    //        int i = 1/0;
    return "success";
}

RmTwoInterfaceImpl

package com.dandan.seatatwo.service;

import io.seata.rm.tcc.api.BusinessActionContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;

@Component
public class RmTwoInterfaceImpl implements RmTwoInterface {

    @Override
    @Transactional
    public String rm2(BusinessActionContext businessActionContext) {
        System.out.println("rm2 try");
//        System.out.println(1/0);

        return null;
    }

    @Override
    @Transactional
    public boolean rm1Commit(BusinessActionContext businessActionContext) {
        System.out.println("rm2 confirm");
        return true;
    }

    @Override
    @Transactional
    public boolean rm1Rollback(BusinessActionContext businessActionContext) {
        System.out.println("rm2 rollback");
        return true;
    }
}

性能比较