引入Prometheus
用 Prometheus 監控應用
1. 用 docker 啟動 Prometheus
編輯配置位置,我將 prometheus.yaml 和 targets.json 文件放在了 /opt/prometheus/conf目錄下
prometheus.yaml
global:scrape_interval: 15s # 抓取間隔evaluation_interval: 15s # 評估間隔scrape_configs:- job_name: 'file_ds'file_sd_configs:- files:- 'targets.json'
由于 prometheus.yaml 文件中,用到了 targets.json 文件,因此,引入 targets.json文件
targets.json
[{"targets":["192.168.10.20:9081"],"labels": {"job": "user-api","app": "user-api","env": "test","instance": "192.168.10.20:8888"} }
]
我的應用是啟動在 宿主機的 8888 端口,因此,我這里寫了宿主機的 ip 和端口。
上面的 9081 端口,可以隨便寫。但是要與應用中的配置一致,看后面配置。
docker run -d --name prometheus --dns=192.168.10.20 -p 9090:9090 -v /opt/prometheus/conf/prometheus.yaml:/etc/prometheus/prometheus.yml -v /opt/prometheus/conf/targets.json:/etc/prometheus/targets.json quay.io/prometheus/prometheusdocker run -d --name prometheus --network host -v /opt/prometheus/conf/prometheus.yaml:/etc/prometheus/prometheus.yml -v /opt/prometheus/conf/targets.json:/etc/prometheus/targets.json quay.io/prometheus/prometheus
下面兩條命令都可以啟動 Prometheus。
2. 啟動應用
修改應用的配置文件 userapi/etc/userapi-api.yaml 文件。
增加了 Prometheus 配置,端口號與 targets.json 文件中的 targets 條目標識的端口號保持一致。
3.測試
訪問 http://192.168.10.20:9090/targets?search=
這樣可以看到 Prometheus 監控了應用。
引入 jaeger
jaeger 是一個用于鏈路追蹤的中間件。
1. docker 啟動 jaeger
docker run -d -e COLLECTOR_ZIPKIN_HTTP_PORT=9411 -p 5775:5775/udp -p 6831:6831/udp -p 6832:6832/udp -p 5778:5778 -p 16686:16686 -p 14268:14268 -p 14269:14269 -p 9411:9411 jaegertracing/all-in-one:latest
這樣啟動的 jaeger,數據默認是放在內存中的。可以根據直接的需求,選擇將數據放在 elasticsearch或其它存儲中。
- 其中 16686 是 ui 端口,直接訪問 http://localhost:16686 便可以進入到 ui 界面
2. 修改 userapi/etc/userapi-api.yaml 文件
在配置文件中加入如下配置:
Telemetry:Name: user-apiEndpoint: http://localhost:14268/api/tracesSampler: 1.0Batcher: jaeger
3. 測試
啟動 userapi 應用,用postman 訪問接口。
這樣就可以在 jaeger 的 ui 上看到訪問的接口。
分布式事務
分布式事務也是微服務架構中必不可少的一部分。go-zero 使用了dtm的方案來解決分布式事務問題。
1. 引入 DTM
官網鏈接
1. github clone 項目
項目地址:https://github.com/dtm-labs/dtm.git
2. 進入項目
創建 conf.yml 配置文件,加入如下配置:
MicroService: # gRPC/HTTP based microservice configDriver: 'dtm-driver-gozero' # name of the driver to handle register/discoverTarget: 'etcd://localhost:2379/dtmservice' # register dtm server to this urlEndPoint: 'localhost:36790'
3. 啟動dtm
從源碼處啟動 dtm
go run main.go -c conf.yml
2. 創建表
在當前使用的微服務對應的數據中創建表。我們曾在 go-zero實戰(2)中創建過zero-mall數據庫。同樣在該數據庫創建 barrier表。
create table if not exists barrier(id bigint(22) primary key auto_increment,trans_type varchar(45),gid varchar(128),branch_id varchar(128),op varchar(45),barrier_id varchar(45),reason varchar(45),create_time datetime default now(),update_time datetime default now(),key(create_time),key(update_time),unique key(gid, branch_id, op, barrier_id)
);
3. 創建積分服務
1. 在 zero_mall 數據庫中 創建 user_score 表
create table user_score(id bigint(0) not null auto_increment,user_id bigint(0) not null,score int(0) not null,primary key(id) using btree
);
2. 創建 user_score.proto 文件
syntax = "proto3";package userscore;option go_package = "./score";message UserScoreRequest {int64 userId = 1;int32 score = 2;
}message UserScoreResponse {int64 userId = 1;int32 score = 2;
}service UserScore {rpc saveScore(UserScoreRequest) returns(UserScoreResponse);rpc saveScoreCallback(UserScoreRequest) returns(UserScoreResponse);
}
為了使用 dmt 實現的分布式事務,saveScore 方法,需要有一個相應的 Callback 方法。為了在發生異常回滾時,執行該方法。
3. 生成代碼
goctl rpc protoc user_score.proto --go_out=./types --go-grpc_out=./types --zrpc_out=.
4. 整理代碼
- 將 生成的 user_score.pb.go 文件和 user_score_grpc.pb.go 文件放入 rpc-common 工程的score目錄下。
- 將 userscore.go 文件放入 rpc-common 工程的 userscore 目錄下。
- 在user-score 創建 go.mod 文件,如下:
module user-scorego 1.22.2
- 在 mall 工程下,執行如下命令:
go work use user-score # 加入 workspace go mod tidy # 下載依賴
- 在 user-score 創建 database 目錄,并增加 sqlx.go 文件,參考 go-zero 實戰(3)
- 將user 微服務 user/internal 目錄下的 dao、repo、model三個目錄復制一份到 user-score 微服務的 user-score/internal 目錄下并做相應的命名修改。
- 修改 user-score/etc/userscore.yaml 文件
Name: score.rpc ListenOn: 127.0.0.1:8081 Etcd:Hosts:- 127.0.0.1:2379Key: score.rpc Mysql:Datasource: root:thinker@tcp(127.0.0.1:3306)/zero_mall?charset=utf8mb4&parseTime=True&loc=Asia%2FShanghaiCacheRedis:- Host: 127.0.0.1:6379Pass: thinkerType: node
- 修改 user-score/internal/svc/servicecontext.go 文件,加入 UserScoreRepo
- 修改生成的 savescorelogic.go 和 savescorecallbacklogic.go 文件
到此,積分服務創建完成。
4. 在 userapi 中調用積分服務
1. 修改 userapi/etc/userapi-api.yaml 文件
加入 user-score 微服務的 rpc 配置
UserScoreRpc:Etcd:Hosts:- 127.0.0.1:2379Key: score.rpc
2. 修改 userapi/intrenal/config/config.go 文件
在這里加入 user-score 微服務的 rpc。
3. 在 userapi/internal/svc/servicecontext.go 文件為上一步增加的變量賦值
因為userapi 是作為 rpc 客戶端,而 user-score 微服務是 rpc 服務端。并且這兩個服務都會用到公共的部分,于是,將公共部分抽取到 rpc-common 下。這樣,userapi 微服務不會用到 user-score 微服務下的代碼。
4. 修改 userapi/internal/logic/userapilogic.go 文件
我們修改了 Register 接口,增加調用 user-score 微服務的代碼。
到此,算是正常走通了。在 userapi 微服務下,注冊功能實現時,同時調用了 user服務和 user-score 服務。
5. 測試
代碼,這部分代碼提交到了 score 分支。
6. 使用 DTM
在上面的注冊功能里,從userapi 遠程調用了 user 和 user-score 兩個服務。試想,如果有一個微服務調用錯誤,顯然不會影響到另一個。我們引入分布式事務,就為了解決在注冊成功后,用戶能夠增加積分。如果積分增加失敗的情況下,也要保證注冊不成功。
1. 在項目中導入 dtm
分別在 /mall/userapi、/mall/user、/mall/user-score 下執行命令:
go get github.com/dtm-labs/dtm
2. 項目中加入dtm 驅動
-
修改 userapi/internal/logic/userapilogic.go 文件
加入驅動_ "github.com/dtm-labs/dtmdriver-gozero"// 這里的地址在文章 分布式事務 1.2 這個步驟,修改配置文件時候,指定的地址// 先上看 1.2 步驟可以找到var dtmServer = "etcd://localhost:2379/dtmservice"
3. 修改 userapi/internal/userapilogic.go 文件
修改 Register 方法的邏輯,引入 dtm
func (l *UserLogic) Register(req *types.Request) (resp *types.Response, err error) {gid := dtmgrpc.MustGenGid(dtmServer)sagaGrpc := dtmgrpc.NewSagaGrpc(dtmServer, gid)userServer, err := l.svcCtx.Config.UserRpc.BuildTarget()if err != nil {return nil, err}userScoreServer, err := l.svcCtx.Config.UserScoreRpc.BuildTarget()if err != nil {return nil, err}userReq := &user.UserRequest{Id: req.Id,Name: req.Name,Gender: req.Gender,}// call save methodsagaGrpc.Add(userServer+"/user.User/save", userServer+"/user.User/saveCallback", userReq)// 這個地方,應該是傳入一個User,因為遠程調用拿不到返回值。暫且先寫死,為了測試效果。userScoreReq := &score.UserScoreRequest{UserId: req.Id,Score: 10,}sagaGrpc.Add(userScoreServer+"/userscore.UserScore/saveScore", userScoreServer+"/userscore.UserScore/saveScoreCallback", userScoreReq)sagaGrpc.WaitResult = trueerr = sagaGrpc.Submit()if err != nil {fmt.Println("---------------------------")fmt.Println(err)return nil, err}//fmt.Sprintf("register add score %d \n", userScore.Score)return &types.Response{Message: "success",Data: "",}, nil
}
核心代碼,就是將原來直接的rpc 調用,委托給 dtm 調用。
4. 修改服務端
- 修改 user-score/internal/logic/savescorelogic.go 文件
引入 dtm - 修改 user/internal/logic/userlogic.go 文件
5. 測試
指定id,插入用于,測試成功。
6. 測試事務
現在模擬 user-score 服務的邏輯出現了問題。檢查事務是否生效。
我們用 postman測試后,發現, user 表中插入了新的數據,但是積分表中是沒有新數據的。那是否事務沒有生效呢?
當從后臺打印的日志可以看出,saveCallback 方法被調用。
這里需要明白一點。
userapi/internal/logic/userapilogic.go 代碼邏輯如下:
代碼提交到了 dtm 分支。代碼