网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
在转入操作失败的情况下,`TransIn`和`TransOut`的补偿操作被执行,保证了最终的余额和转账前是一样的。
## 3 go-zero使用dtm参考代码
### 3.1 go-zero支持dtm 代码操作步骤
参考:[go-zero支持dtm]( ):官方讲解+代码
**注意**:这里只是从调用方角度展示了如何使用dtm的代码,而关于被调用方的代码缺失,需要结合`3.2 gozerodtm 代码操作步骤`。
1. 启动`Etcd`,查看所有`key`命令:`etcdctl.exe get --prefix ""`
2. 把[dtm]( ) `clone`下来
3. 找到`dtm`项目根文件夹下的`conf.sample.yml`,复制一份名称改为`conf.yml`
4. 把`conf.yml`中的下面这段注释放开:
MicroService: # gRPC/HTTP based microservice config
Driver: ‘dtm-driver-gozero’ # name of the driver to handle register/discover
Target: ‘etcd://localhost:2379/dtmservice’ # register dtm server to this url
EndPoint: ‘localhost:36790’
* `MicroService`:这个不要动,这个代表要对把`dtm`注册到那个微服务服务集群里面去,使微服务集群内部服务可以通过`grpc`直接跟`dtm`交互
* `Driver` :`'dtm-driver-gozero'`, 使用`go-zero`的注册服务发现驱动,支持`go-zero`。
* `Target`: `'etcd://localhost:2379/dtmservice'`将当前`dtm`的`server`直接注册到微服务所在的`etcd`集群中,如果`go-zero`作为微服务使用的话,就可以直接通过`etcd`拿到`dtm`的`server grpc`链接,直接就可以跟`dtm server`交互了。
* `EndPoint`: `'localhost:36790'` , 代表的是`dtm`的`server`的连接地址+端口 , 集群中的微服务可以直接通过`etcd`获得此地址跟`dtm`交互了,如果你自己去改了`dtm`源码`grpc`端口,记得这里要改下端口。
5. 启动`dtm`
前提:配置好conf.yml
go run main.go -c conf.yml
如果是用`Goland`启动,则需要将 `-c conf.yml`作为参数填写到`Program arguments`中。
6. 运行一个`go-zero`的服务
git clone https://github.com/dtm-labs/dtmdriver-clients && cd dtmdriver-clients
cd gozero/trans && go run trans.go
7. 发起一个`go-zero`使用`dtm`的事务
在dtmdriver-clients的目录下
cd gozero/app && go run main.go
8. 在`trans`的日志中看到记录信息,就是事务正常完成了
2023/07/24 21:45:47 transfer out 30 cents from 1
2023/07/24 21:45:47 transfer in 30 cents to 2
2023/07/24 21:45:47 transfer out 30 cents from 1
2023/07/24 21:45:47 transfer out 30 cents from 1
### ※3.2 gozerodtm 代码操作步骤
参考:[gozerodtm]( ):Mikaelemmmm代码
**项目介绍:**
* `order-api`是`http`服务入口。
* `order-srv`是订单的`rpc`服务,与`dtm-gozero-order`数据库中`order`表交互。
* `stock-srv`是库存的`rpc`服务,与`dtm-gozero-stock`数据库中`stock`表交互。
**整体流程:**
`http`调用`order-api`中立即下单接口,然后`order-api`立即下单接口会去调用`order-srv`创建订单并且调用`stock-srv`扣减库存,因为`order-srv`与`stock-srv`是2个独立`grpc`服务,所以使用`dtm`来做分布式事务协调。
前5步与3.1完全一致。
1. 创建数据库表
在项目的`datasql`目录下分别有:`order-srv`服务的`dtm-gozero-order.sql`、`stock-srv`服务的`dtm-gozero-stock.sql`,在数据库按照创建两个数据库并执行相关`sql`。
2. 修改数据库连接配置
分别修改 `order-srv`、`stock-srv`服务的`yaml`中的数据库连接配置。
3. 修改启动类读取配置文件的路径
分别修改`order-api`、`order-srv`、`stock-srv`中读取`yaml`配置的路径。
4. 启动服务
依次启动`order-srv`、`stock-srv`、`order-api`服务
5. 请求测试
使用`POSTMAN`测试:
请求地址:<http://localhost:8889/order/quickCreate>
请求方式:`POST`
数据格式:`JSON`
请求数据:`{"userId":2,"goodsId":1,"num":20}`
6. 测试结果
HTTP请求响应成功,状态是200,但是查看[DTM服务]( )发现,操作失败,且数据库中数据未改变。
原因排查:
依次查看`order-api`、`order-srv`、`stock-srv`服务后,发现在`order-srv`服务有报错,主要报错信息:
{“level”:“error”,“ts”:“2023-07-25T14:08:24.891+0800”,“caller”:“dtmimp/utils.go:207”,“msg”:"used: 0 ms exec error: Error 1146: Table ‘dtm_barrier.barrier’ doesn’t exist for insert i
gnore into dtm_barrier.barrier(trans_type, gid, branch_id, op, barrier_id, reason) values(?,?,?,?,?,?)
推断是缺少`dtm_barrier`数据库以及`barrier`数据表。
7. 创建`dtm_barrier`数据库以及`barrier`数据表
/*
Navicat Premium Data Transfer
Source Server : 本机
Source Server Type : MySQL
Source Server Version : 50737
Source Host : 127.0.0.1:3357
Source Schema : dtm_barrier
Target Server Type : MySQL
Target Server Version : 50737
File Encoding : 65001
Date: 25/07/2023 14:06:43
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
– Table structure for barrier
DROP TABLE IF EXISTS barrier
;
CREATE TABLE barrier
(
id
bigint(20) NOT NULL AUTO_INCREMENT COMMENT ‘ID’,
trans\_type
varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
gid
varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
branch\_id
varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
op
varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
barrier\_id
bigint(20) NULL DEFAULT NULL,
reason
varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
create\_time
datetime(0) NULL DEFAULT CURRENT_TIMESTAMP(0) ON UPDATE CURRENT_TIMESTAMP(0),
PRIMARY KEY (id
) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = ‘dtm子屏障数据表’ ROW_FORMAT = DYNAMIC;
SET FOREIGN_KEY_CHECKS = 1;
8. 重新请求测试
HTTP请求响应成功,状态是200,[DTM服务]( )中状态显示操作成功,且数据库中数据改变。
## 4 注意事项
在`3.1 go-zero支持dtm 代码操作步骤`和`※3.2 gozerodtm 代码操作步骤`有一些操作注意事项。
**重点是子事务如何处理 `回滚`、`补偿`等操作,这一块需要特别留意。**
### 4.1 grpc接口地址
在找grpc访问的方法路径时候,是在`***.pb.go`的文件中找该方法`invoke`的路径。
原因:当proto文件方法名字都是大写,这2者都一样如果proto中方法名字小写的,invoke中跟他的方法名就不一样了
![在这里插入图片描述](https://img-blog.csdnimg.cn/a2ee32738c7743478026f01a5bd6fa06.png)
仔细观察发现两个路径有大小写之分!
![在这里插入图片描述](https://img-blog.csdnimg.cn/976fcaed8fe8410a88ae47f2cb340455.png)
### ※4.2 动态调用过程
在go-zero使用dtm的分布式事务时,许多的调用是从dtm服务器发起的,例如TCC的Confirm/Cancel,SAGA/MSG的所有调用。
dtm无需知道组成分布式事务的相关业务api的强类型,它是动态的调用这些api。
grpc的调用,可以类比于HTTP的POST,其中:
* c.BuildTarget() 产生的target类似于URL中的Host
* “/trans.TransSvc/TransOut” 相当于URL中的Path
* &busi.BusiReq{Amount: 30, UserId: 1} 相当于Post中Body
* pb.Response 相当于HTTP请求的响应
通过下面这部分代码,dtm就拿到了完整信息,就能够发起完整的调用了
Add(busiServer+“/trans.TransSvc/TransOut”, &busi.BusiReq{Amount: 30, UserId: 1})
### 4.3 dtm的回滚补偿
在使用dtm的grpc时候,当我们使用saga、tcc等如果第一步尝试或者执行失败了,是希望它能执行后面的rollback的,在grpc中的服务如果发生错误了,必须返回 : status.Error(codes.Aborted, dtmcli.ResultFailure) , 返回其他错误,不会执行你的rollback操作,dtm会一直重试,如下图:
![在这里插入图片描述](https://img-blog.csdnimg.cn/effc98c9db45461bb2f181ecb78ffc67.png)
### 4.4 `barrier`的空补偿、悬挂等
这一步需要创建`dtm_barrier`数据库以及`barrier`数据表。这个其实就是为我们的业务服务做了一个检查,防止空补偿,具体可以看barrier.Call中源码,没几行代码可以看懂的。
每个与`db`交互的服务只要用到了`barrier`,都需要这个。
### 4.5 `barrier`在`rpc`中本地事务
在rpc的业务中,如果使用了barrier的话,那么在model中与db交互时候必须要用事务,并且一定要跟barrier用同一个事务。
logic:
![在这里插入图片描述](https://img-blog.csdnimg.cn/9a53a1c862c240e1bfc90b706ec92a00.png#pic_center)
model:
![在这里插入图片描述](https://img-blog.csdnimg.cn/594b5638ef9841b1833e2016df77e93b.png#pic_center)
![img](https://img-blog.csdnimg.cn/img_convert/9e35142f76bdba9abfd7b51e8b2380f8.png)
![img](https://img-blog.csdnimg.cn/img_convert/2b7b09a5cc21f5098e2035e638cd7ed1.png)
**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**
**[需要这份系统化的资料的朋友,可以添加戳这里获取](https://bbs.csdn.net/topics/618658159)**
**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**
存中...(img-KXBf29Ri-1715812809611)]
**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**
**[需要这份系统化的资料的朋友,可以添加戳这里获取](https://bbs.csdn.net/topics/618658159)**
**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**