seataAT模式实现分布式事务

1、包含服务

nacos注册中心,配置中心

seata server

微服务A,微服务B

2、下载资源

Seata Releases

seataAT模式实现分布式事务_第1张图片

3、服务启动

1)nacos服务启动:startup.cmd -m standalone

2)配置seata

主要包括:

①修改register.conf文件,注册服务至nacos上

②修改config.txt,同步配置至nacos

③创建seata相关数据库表

1、解压seata-server-1.4.1.zip,进入seata/conf,修改register.conf文件如下:

registry {
  # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
  type = "nacos"

  nacos {
    application = "seata-server"
    serverAddr = "127.0.0.1:8848"
    group = "SEATA_GROUP"
    namespace = "4b24821e-b14b-43a7-b3a0-9480520ebb59"
    cluster = "default"
    username = "nacos"
    password = "nacos"
  }

}

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

  nacos {
    serverAddr = "127.0.0.1:8848"
    namespace = "4b24821e-b14b-43a7-b3a0-9480520ebb59"
    group = "SEATA_GROUP"
    username = "nacos"
    password = "nacos"
  }
 
}

进入/bin目录执行

seata-server.bat -p 9000 -m file

seataAT模式实现分布式事务_第2张图片

2、解压Source code,进入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.vgroupMapping.spring.slivloon-cloud=default
service.default.grouplist=127.0.0.1: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.defaultGlobalTransactionTimeout=60000
client.tm.degradeCheck=false
client.tm.degradeCheckAllowTimes=10
client.tm.degradeCheckPeriod=2000
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
store.db.datasource=druid
store.db.dbType=mysql
store.db.driverClassName=com.mysql.jdbc.Driver
store.db.url=jdbc:mysql://127.0.0.1:3306/ali-seata?useUnicode=true
store.db.user=root
store.db.password=ares123
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
log.exceptionRate=100
transport.serialization=seata
transport.compressor=none
metrics.enabled=false
metrics.registryType=compact
metrics.exporterList=prometheus
metrics.exporterPrometheusPort=9898

主要修改service.vgroupMapping.spring.slivloon-cloud=default,标红为自定义事务分组,之后各服务配置文件需与之对应。其他配置自己看着改

在git环境下执行script\config-center\nacos中的nacos-config.sh,使配置同步至nacos配置中心(也可通过python环境执行py脚本)

sh nacos-config.sh -h localhost -p 8848 -t 56c94dbe-7fbe-49c2-b456-170001455569 -u nacos -w nacos

seataAT模式实现分布式事务_第3张图片

3、初始化seata相关mysql表

执行script\server\db目录下,mysql.sql文件

seataAT模式实现分布式事务_第4张图片

 

4、整合springcloud

搭建简单的微服务seata-pro1和seata-pro2,这里使用springdata jpa进行数据交互,仅展示主要代码部分。

业务逻辑为seata-pro1生成订单,通过feign调用seata-pro2的增加积分功能。

1、添加依赖


  com.alibaba.cloud
  spring-cloud-starter-alibaba-seata

2、修改配置文件

application.yml

spring:
  application:
    name: seata-pro1
  cloud:
    nacos:
      #nacoss config
      discovery:
        server-addr: localhost:8848
        namespace: 4b24821e-b14b-43a7-b3a0-9480520ebb59
ribbon:
  ReadTimeout: 30000
  ConnectTimeout: 30000

TIPS,测试时分支事务打断点,由于feign连接超时会报timeout错误,所以可添加ribbon相关超时配置

bootstrap.yml

spring:
  cloud:
    nacos:
      config:
        #nacos配置中心
        server-addr: localhost:8848
        extension-configs[0]:
          data-id: db.yaml
          group: dev
          refresh: true
seata:
  enabled: true
  application-id: ${spring.application.name}
  tx-service-group: spring.slivloon-cloud    #此处配置自定义的seata事务分组名称
  enable-auto-data-source-proxy: true    #开启数据库代理
  config:
    type: nacos
    nacos:
      server-addr: ${spring.cloud.nacos.discovery.server-addr}
      namespace: ${spring.cloud.nacos.discovery.namespace}
      group: SEATA_GROUP
  registry:
    type: nacos
    nacos:
      application: seata-server
      server-addr: ${spring.cloud.nacos.discovery.server-addr}
      namespace: ${spring.cloud.nacos.discovery.namespace}

3、配置代理数据源

@Configuration
@ConditionalOnClass(HikariDataSource.class)
public class DataSourceProxyConfig {

    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.hikari")
    public DataSource hikariDataSource() {
        HikariDataSource hikariDataSource = new HikariDataSource();
        return hikariDataSource;
    }

    @Primary
    @Bean("dataSource")
    public DataSourceProxy dataSourceProxy(DataSource hikariDataSource) {
        return new DataSourceProxy(hikariDataSource);
    }

}

4、seata-pro1服务

实体类order订单

Table(name = "sliv_order")
@Data
@Entity
@DynamicUpdate
public class Order extends BaseEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    @Column(columnDefinition= "int(5) comment '买家id'")
    private Integer buyerId;

    @Column(columnDefinition= "int(1) comment '订单状态(0进行中,1已完成,2已取消)'")
    private Integer status;

    @Column(columnDefinition= "decimal(12,2) comment '订单金额'")
    private Float payAmount;

    @Column(columnDefinition= "varchar(90) comment '备注'")
    private String remark;

}

service服务层

@Service
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class OrderService {

    private final OrderDao orderDao;

    private final CreditFeign creditFeign;

    @GlobalTransactional
    public void save(OrderDto orderDto){
        Order order = new Order();
        BeanUtils.copyProperties(orderDto, order);
        this.orderDao.saveOne(order);
        Bonus build = Bonus.builder()
                .buyerId(orderDto.getBuyerId())
                .bonus(orderDto.getPayAmount())
                .build();
        this.creditFeign.increaseAmount(build);
    }
}

feign服务调用

@FeignClient(name = "seata-pro2")
public interface CreditFeign {
    @PostMapping("/credit/increase")
    void increaseAmount(@RequestBody Bonus bonus);
}

controller

@RestController
@RequestMapping("/order")
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class OrderController {

    private final OrderService orderService;

    @PostMapping("/save")
    public Result send(OrderDto dto){
        this.orderService.save(dto);
        return Result.ofSuccess();
    }
}

5、seata-pro2服务

实体类credit积分

@Table(name = "sliv_credit")
@Data
@Entity
@DynamicUpdate
public class Credit extends BaseEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    @Column(columnDefinition= "int(5) comment '买家id'")
    private Integer buyerId;

    @Column(columnDefinition= "int(1) comment '积分等级(0青铜,1王者)'")
    private Integer level;

    @Column(columnDefinition= "decimal(12,2) comment '积分数'")
    private Float creditAmount;

    @Column(columnDefinition= "varchar(90) comment '备注'")
    private String remark;
}

service服务层

@Service
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class CreditService {

    private final CreditDao creditDao;

  
    public void increaseAmount(Bonus bonus){
        Credit credit = this.creditDao.findByBuyerId(bonus.getBuyerId());
        if (credit != null) {
            Float creditAmount = credit.getCreditAmount();
            credit.setCreditAmount(creditAmount+bonus.getBonus());
            this.creditDao.updateOne(credit);
        }
    }

}

feign接口

@RestController
@RequestMapping("/credit")
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class CreditFeignController {

    private final CreditService creditService;

    @PostMapping("/increase")
    public void increaseAmount(@RequestBody Bonus bonus){
       this.creditService.increaseAmount(bonus);
    }
}

6测试

无异常情况

seataAT模式实现分布式事务_第5张图片

seataAT模式实现分布式事务_第6张图片

seataAT模式实现分布式事务_第7张图片

异常情况测试

seataAT模式实现分布式事务_第8张图片

seataAT模式实现分布式事务_第9张图片

TIPS:有个小坑,当微服务中定义了全局异常捕获@RestControllerAdvice或者使用feign降级处理,分支事务发生异常时,本地事务无法进行回滚。

你可能感兴趣的:(seataAT模式实现分布式事务)