分布式事务

在 Dubbo 中使用分布式事务

使用方式

第一步

首先访问: https://seata.io/zh-cn/blog/download.html

下载我们需要使用的 seata1.5.2 服务

第二步

1.在你的参与全局事务的数据库中加入 undo_log 这张表(TCC,SAGA,XA 可跳过这步)

  1. -- for AT mode you must to init this sql for you business database. the seata server not need it.
  2. CREATE TABLE IF NOT EXISTS `undo_log`
  3. (
  4. `branch_id` BIGINT(20) NOT NULL COMMENT 'branch transaction id',
  5. `xid` VARCHAR(100) NOT NULL COMMENT 'global transaction id',
  6. `context` VARCHAR(128) NOT NULL COMMENT 'undo_log context,such as serialization',
  7. `rollback_info` LONGBLOB NOT NULL COMMENT 'rollback info',
  8. `log_status` INT(11) NOT NULL COMMENT '0:normal status,1:defense status',
  9. `log_created` DATETIME(6) NOT NULL COMMENT 'create datetime',
  10. `log_modified` DATETIME(6) NOT NULL COMMENT 'modify datetime',
  11. UNIQUE KEY `ux_undo_log` (`xid`, `branch_id`)
  12. ) ENGINE = InnoDB
  13. AUTO_INCREMENT = 1
  14. DEFAULT CHARSET = utf8 COMMENT ='AT transaction mode undo table';

2.在你的 mysql 数据库中创建名为 seata 的库,并使用以下下 sql

  1. -- -------------------------------- The script used when storeMode is 'db' --------------------------------
  2. -- the table to store GlobalSession data
  3. CREATE TABLE IF NOT EXISTS `global_table`
  4. (
  5. `xid` VARCHAR(128) NOT NULL,
  6. `transaction_id` BIGINT,
  7. `status` TINYINT NOT NULL,
  8. `application_id` VARCHAR(32),
  9. `transaction_service_group` VARCHAR(32),
  10. `transaction_name` VARCHAR(128),
  11. `timeout` INT,
  12. `begin_time` BIGINT,
  13. `application_data` VARCHAR(2000),
  14. `gmt_create` DATETIME,
  15. `gmt_modified` DATETIME,
  16. PRIMARY KEY (`xid`),
  17. KEY `idx_gmt_modified_status` (`gmt_modified`, `status`),
  18. KEY `idx_transaction_id` (`transaction_id`)
  19. ) ENGINE = InnoDB
  20. DEFAULT CHARSET = utf8;
  21. -- the table to store BranchSession data
  22. CREATE TABLE IF NOT EXISTS `branch_table`
  23. (
  24. `branch_id` BIGINT NOT NULL,
  25. `xid` VARCHAR(128) NOT NULL,
  26. `transaction_id` BIGINT,
  27. `resource_group_id` VARCHAR(32),
  28. `resource_id` VARCHAR(256),
  29. `branch_type` VARCHAR(8),
  30. `status` TINYINT,
  31. `client_id` VARCHAR(64),
  32. `application_data` VARCHAR(2000),
  33. `gmt_create` DATETIME(6),
  34. `gmt_modified` DATETIME(6),
  35. PRIMARY KEY (`branch_id`),
  36. KEY `idx_xid` (`xid`)
  37. ) ENGINE = InnoDB
  38. DEFAULT CHARSET = utf8;
  39. -- the table to store lock data
  40. CREATE TABLE IF NOT EXISTS `lock_table`
  41. (
  42. `row_key` VARCHAR(128) NOT NULL,
  43. `xid` VARCHAR(96),
  44. `transaction_id` BIGINT,
  45. `branch_id` BIGINT NOT NULL,
  46. `resource_id` VARCHAR(256),
  47. `table_name` VARCHAR(32),
  48. `pk` VARCHAR(36),
  49. `gmt_create` DATETIME,
  50. `gmt_modified` DATETIME,
  51. PRIMARY KEY (`row_key`),
  52. KEY `idx_branch_id` (`branch_id`)
  53. ) ENGINE = InnoDB
  54. DEFAULT CHARSET = utf8;

第三步

在你的项目中引入 seata 依赖

spring-boot 应用:

  1. <dependency>
  2. <groupId>io.seata</groupId>
  3. <artifactId>seata-spring-boot-starter</artifactId>
  4. <version>1.5.2</version>
  5. </dependency>

spring 应用:

  1. <dependency>
  2. <groupId>io.seata</groupId>
  3. <artifactId>seata-all</artifactId>
  4. <version>1.5.2</version>
  5. </dependency>

第四步

spring-boot 应用:

参考 seata/script/client/spring at develop · seata/seata (github.com)

加到你项目的 application.yml中.

  1. seata:
  2. enabled: true
  3. application-id: applicationName
  4. tx-service-group: my_test_tx_group
  5. enable-auto-data-source-proxy: true #仅AT与XA模式需要为true,开启后会自动代理数据源
  6. data-source-proxy-mode: AT #可选AT&XA
  7. config:
  8. type: nacos
  9. nacos:
  10. #namespace: 如果配置创建在非默认namespace,请在此处填写namespace的id
  11. serverAddr: 127.0.0.1:8848
  12. group: SEATA_GROUP
  13. username: "nacos"
  14. password: "nacos"
  15. data-id: seata.properties
  16. registry:
  17. type: nacos
  18. nacos:
  19. application: seata-server
  20. server-addr: 127.0.0.1:8848
  21. group: SEATA_GROUP
  22. #namespace: 如果配置创建在非默认namespace,请在此处填写namespace的id
  23. username: "nacos"
  24. password: "nacos"

spring 应用:

添加 seata/script/client/conf at develop · seata/seata (github.com) 下 registry.conf,由于高可用部署使用第三方配置中心,故无需 file.conf

  1. registry {
  2. # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa、custom
  3. type = "nacos"
  4. nacos {
  5. application = "seata-server"
  6. serverAddr = "127.0.0.1:8848"
  7. group = "SEATA_GROUP"
  8. namespace = ""
  9. username = ""
  10. password = ""
  11. ##if use MSE Nacos with auth, mutex with username/password attribute
  12. #accessKey = ""
  13. #secretKey = ""
  14. ##if use Nacos naming meta-data for SLB service registry, specify nacos address pattern rules here
  15. #slbPattern = ""
  16. }
  17. }
  18. config {
  19. # file、nacos 、apollo、zk、consul、etcd3、springCloudConfig、custom
  20. type = "nacos"
  21. nacos {
  22. serverAddr = "127.0.0.1:8848"
  23. namespace = ""
  24. group = "SEATA_GROUP"
  25. username = ""
  26. password = ""
  27. ##if use MSE Nacos with auth, mutex with username/password attribute
  28. #accessKey = ""
  29. #secretKey = ""
  30. dataId = "seata.properties"
  31. }
  32. }

第五步

运行你下载的 nacos,并参考 https://github.com/seata/seata/tree/develop/script/config-center 的 config.txt 并修改

  1. #仅client使用
  2. #事务分组叫my_test_tx_group对应的seata-server集群为default
  3. service.vgroupMapping.my_test_tx_group=default
  4. #以下仅server使用
  5. store.mode=db
  6. store.db.datasource=druid
  7. store.db.dbType=mysql
  8. store.db.driverClassName=com.mysql.jdbc.Driver
  9. store.db.url=jdbc:mysql://127.0.0.1:3306/seata?useUnicode=true
  10. store.db.user=username
  11. store.db.password=password
  12. store.db.minConn=5
  13. store.db.maxConn=30
  14. store.db.globalTable=global_table
  15. store.db.branchTable=branch_table
  16. store.db.queryLimit=100
  17. store.db.lockTable=lock_table
  18. store.db.maxWait=5000

打开 nacos 控制台,在对应的 namespace 下创建 dataId 为 seata.properties 的配置,并填写 group 为 SEATA_GROUP,并将以上内容填入选择类型为 properties 保存 Dingtalk_20220724021635.jpg.png

第六步

更改 server 中的 application.yml

  1. server:
  2. port: 7091
  3. spring:
  4. application:
  5. name: seata-server
  6. logging:
  7. config: classpath:logback-spring.xml
  8. file:
  9. path: ${user.home}/logs/seata
  10. console:
  11. user:
  12. username: seata
  13. password: seata
  14. seata:
  15. config:
  16. # support: nacos, consul, apollo, zk, etcd3
  17. type: nacos
  18. nacos:
  19. server-addr: 127.0.0.1:8848
  20. #namespace: 如果配置创建在非默认namespace,请在此处填写namespace的id
  21. group: SEATA_GROUP
  22. username:
  23. password:
  24. ##if use MSE Nacos with auth, mutex with username/password attribute
  25. #access-key: ""
  26. #secret-key: ""
  27. data-id: seata.properties
  28. registry:
  29. # support: nacos, eureka, redis, zk, consul, etcd3, sofa
  30. type: nacos
  31. nacos:
  32. application: seata-server
  33. server-addr: 127.0.0.1:8848
  34. group: SEATA_GROUP
  35. namespace:
  36. cluster: default
  37. #namespace: 如果配置创建在非默认namespace,请在此处填写namespace的id
  38. password:
  39. ##if use MSE Nacos with auth, mutex with username/password attribute
  40. #access-key: ""
  41. #secret-key: ""
  42. # server:
  43. # service-port: 8091 #If not configured, the default is '${server.port} + 1000'
  44. security:
  45. secretKey: SeataSecretKey0c382ef121d778043159209298fd40bf3850a017
  46. tokenValidityInMilliseconds: 1800000
  47. ignore:
  48. urls: /,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.ico,/console-fe/public/**,/api/v1/auth/login

第七步

在全局事务调用者(发起全局事务的服务)的接口上加入 @GlobalTransactional 示例如下:

  1. @GetMapping(value = "testCommit")
  2. @GlobalTransactional
  3. public Object testCommit(@RequestParam(name = "id",defaultValue = "1") Integer id,
  4. @RequestParam(name = "sum", defaultValue = "1") Integer sum) {
  5. Boolean ok = productService.reduceStock(id, sum);
  6. if (ok) {
  7. LocalDateTime now = LocalDateTime.now();
  8. Orders orders = new Orders();
  9. orders.setCreateTime(now);
  10. orders.setProductId(id);
  11. orders.setReplaceTime(now);
  12. orders.setSum(sum);
  13. orderService.save(orders);
  14. return "ok";
  15. } else {
  16. return "fail";
  17. }
  18. }

spring 应用在使用 AT 或 XA 模式下需手动代理数据源选择事务模式和初始化事务扫描器

  1. @Primary
  2. @Bean("dataSource")
  3. public DataSource dataSource(DataSource druidDataSource) {
  4. //AT 代理 二选一
  5. return new DataSourceProxy(druidDataSource);
  6. //XA 代理
  7. return new DataSourceProxyXA(druidDataSource)
  8. }
  1. @Bean
  2. public GlobalTransactionScanner globalTransactionScanner() {
  3. return new GlobalTransactionScanner("应用名", "my_test_tx_group");
  4. }

如果使用 tcc 模式,需要额外在对应的 provider 的 serviceimpl 中定义两阶段的 try 和 confirm(commit) cancel(rollback)

spring-boot 应用需关闭数据源代理

  1. seata:
  2. enable-auto-data-source-proxy: false
  1. /**
  2. * 定义两阶段提交 name = 该tcc的bean名称,全局唯一 commitMethod = commit 为二阶段确认方法 rollbackMethod = rollback 为二阶段取消方法
  3. * useTCCFence=true 为开启防悬挂
  4. * BusinessActionContextParameter注解 传递参数到二阶段中
  5. *
  6. * @param params -入参
  7. * @return String
  8. */
  9. @TwoPhaseBusinessAction(name = "beanName", commitMethod = "commit", rollbackMethod = "rollback", useTCCFence = true)
  10. public void insert(@BusinessActionContextParameter(paramName = "params") Map<String, String> params) {
  11. logger.info("此处可以预留资源,或者利用tcc的特点,与AT混用,二阶段时利用一阶段在此处存放的消息,通过二阶段发出,比如redis,mq等操作");
  12. }
  13. /**
  14. * 确认方法、可以另命名,但要保证与commitMethod一致 context可以传递try方法的参数
  15. *
  16. * @param context 上下文
  17. * @return boolean
  18. */
  19. public void commit(BusinessActionContext context) {
  20. logger.info("预留资源真正处理,或者发出mq消息和redis入库");
  21. }
  22. /**
  23. * 二阶段取消方法
  24. *
  25. * @param context 上下文
  26. * @return boolean
  27. */
  28. public void rollback(BusinessActionContext context) {
  29. logger.info("预留资源释放,或清除一阶段准备让二阶段提交时发出的消息缓存");
  30. }

linux/macos

  1. cd bin
  2. sh seata-server.sh

windows

  1. cd bin
  2. ./seata-server.bat

运行 seata-server,成功后,运行自己的服务 dubbo provider&consumer

第八步高可用 Seata-server 搭建

由于 seata-server 支持计算与存储分离模式,并支持暴露服务地址至多种注册中心,仅需按照第六步配置完毕后水平扩展即可

详情请访问:https://seata.io/

最后修改 December 16, 2022: Fix check (#1736) (97972c1)