本文是對 使用go-zero快速構(gòu)建微服務(wù)[1]的親手實踐
編寫API Gateway代碼
mkdir?bookstore?&&?cd?bookstore
go?mod?init?bookstore
mkdir api && goctl api -o api/bookstore.api
syntax = "v1"
info(
title: "xx使用go-zero"
desc: "xx用來上手go-zero"
author: "xxxx"
email: "xxxx@gmail.com"
)
type (
addReq struct {
book string `form:"book"`
price int64 `form:"price"`
}
addResp struct {
ok bool `json:"ok"`
}
)
type (
checkReq struct {
book string `form:"book"`
}
checkResp struct {
found bool `json:"found"`
price int64 `json:"price"`
}
)
service bookstore-api {
@handler AddHandler
get /add (addReq) returns (addResp)
@handler CheckHandler
get /check (checkReq) returns (checkResp)
}
cd api && goctl api go -api bookstore.api -dir .

api
├──?bookstore.api??????????????????//?api定義
├──?bookstore.go???????????????????//?main入口定義
├──?etc
│???└──?bookstore-api.yaml?????????//?配置文件
└──?internal
????├──?config
????│???└──?config.go??????????????//?定義配置
????├──?handler
????│???├──?addhandler.go??????????//?實現(xiàn)addHandler
????│???├──?checkhandler.go????????//?實現(xiàn)checkHandler
????│???└──?routes.go??????????????//?定義路由處理
????├──?logic
????│???├──?addlogic.go????????????//?實現(xiàn)AddLogic
????│???└──?checklogic.go??????????//?實現(xiàn)CheckLogic
????├──?svc
????│???└──?servicecontext.go??????//?定義ServiceContext
????└──?types
????????└──?types.go???????????????//?定義請求、返回結(jié)構(gòu)體
go run bookstore.go -f etc/bookstore-api.yaml
啟動API Gateway服務(wù),默認偵聽在8888端口
因為默認生成的api/etc/bookstore-api.yml
為:
Name:?bookstore-api
Host:?0.0.0.0
Port:?8888

按提示下載,再次運行:


{"@timestamp":"2023-02-16T16:31:09.658+08:00","caller":"stat/usage.go:61","content":"CPU:?0m,?MEMORY:?Alloc=2.5Mi,?TotalAlloc=2.5Mi,?Sys=14.5Mi,?NumGC=0","level":"stat"}
{"@timestamp":"2023-02-16T16:31:09.662+08:00","caller":"load/sheddingstat.go:61","content":"(api)?shedding_stat?[1m],?cpu:?0,?total:?0,?pass:?0,?drop:?0","level":"stat"}
{"@timestamp":"2023-02-16T16:31:15.044+08:00","caller":"stat/metrics.go:210","content":"(bookstore-api)?-?qps:?0.0/s,?drops:?0,?avg?time:?0.0ms,?med:?0.0ms,?90th:?0.0ms,?99th:?0.0ms,?99.9th:?0.0ms","level":"stat"}
會定時(默認一分鐘)輸出cpu,內(nèi)存等的統(tǒng)計信息,可以通過 logx.DisableStat()
禁用 (可以做到自定義模板.tpl里)

返回的是null,并不是預(yù)期的{"found":false,"price":0}
這是因為:

resp是一個指針,這樣直接return會是nil,需要如下顯式聲明

重啟服務(wù),再次發(fā)起請求,這樣的response就符合預(yù)期了~

目前只返回了個空值,接下來會在rpc服務(wù)里實現(xiàn)業(yè)務(wù)邏輯
可以修改internal/svc/servicecontext.go來傳遞服務(wù)依賴(如果需要,比如Config,Auth,后續(xù)用到的RPC等)
實現(xiàn)邏輯可以修改internal/logic下的對應(yīng)文件(如果接口較多,可以在.api里定義不同的group,使用goctl生成代碼時,會自動在logic下根據(jù)group名稱創(chuàng)建不同的文件夾)
可以通過goctl生成各種客戶端語言的api調(diào)用代碼(供客戶端同學使用;支持多種語言)
編寫RPC代碼
編寫add rpc服務(wù)
切到bookstore目錄下
mkdir -p rpc/add && cd rpc/add
goctl rpc template -o add.proto
修改后文件內(nèi)容如下:
syntax = "proto3";
package add;
option go_package = "./pb";
message addReq {
string book = 1;
int64 price = 2;
}
message addResp {
bool ok = 1;
}
service adder {
rpc add(addReq) returns(addResp);
}
goctl rpc protoc add.proto --go_out=./pb --go-grpc_out=./pb --zrpc_out=.

rpc/add
├──?add.go??????????????????????//?rpc服務(wù)main函數(shù)
├──?add.proto???????????????????//?rpc接口定義
├──?adder
│???├──?adder.go????????????????//?提供了外部調(diào)用方法,無需修改
│???├──?adder_mock.go???????????//?mock方法,測試用
│???└──?types.go????????????????//?request/response結(jié)構(gòu)體定義
├──?etc
│???└──?add.yaml????????????????//?配置文件
├──?internal
│???├──?config
│???│???└──?config.go???????????//?配置定義
│???├──?logic
│???│???└──?addlogic.go?????????//?add業(yè)務(wù)邏輯在這里實現(xiàn)
│???├──?server
│???│???└──?adderserver.go??????//?調(diào)用入口,?不需要修改
│???└──?svc
│???????└──?servicecontext.go???//?定義ServiceContext,傳遞依賴
└──?pb
????└──?add.pb.go
go run add.go -f etc/add.yaml
可運行該服務(wù)
默認每隔一分鐘輸出cpu和內(nèi)存信息
{"@timestamp":"2023-02-16T20:02:10.640+08:00","caller":"stat/usage.go:61","content":"CPU:?0m,?MEMORY:?Alloc=3.3Mi,?TotalAlloc=6.2Mi,?Sys=15.9Mi,?NumGC=3","level":"stat"}
{"@timestamp":"2023-02-16T20:02:10.656+08:00","caller":"load/sheddingstat.go:61","content":"(rpc)?shedding_stat?[1m],?cpu:?0,?total:?0,?pass:?0,?drop:?0","level":"stat"}
編寫check rpc服務(wù)
切到bookstore目錄下
mkdir -p rpc/check && cd rpc/check
goctl rpc template -o check.proto
修改后文件內(nèi)容如下:
syntax = "proto3";
package check;
option go_package = "./pb";
message checkReq {
string book = 1;
}
message checkResp {
bool found = 1;
int64 price = 2;
}
service checker {
rpc check(checkReq) returns(checkResp);
}
goctl rpc protoc check.proto --go_out=./pb --go-grpc_out=./pb --zrpc_out=.

rpc/check
├──?check.go????????????????????//?rpc服務(wù)main函數(shù)
├──?check.proto?????????????????//?rpc接口定義
├──?checker
│???├──?checker.go??????????????//?提供了外部調(diào)用方法,無需修改
│???├──?checker_mock.go?????????//?mock方法,測試用
│???└──?types.go????????????????//?request/response結(jié)構(gòu)體定義
├──?etc
│???└──?check.yaml??????????????//?配置文件
├──?internal
│???├──?config
│???│???└──?config.go???????????//?配置定義
│???├──?logic
│???│???└──?checklogic.go???????//?check業(yè)務(wù)邏輯在這里實現(xiàn)
│???├──?server
│???│???└──?checkerserver.go????//?調(diào)用入口,?不需要修改
│???└──?svc
│???????└──?servicecontext.go???//?定義ServiceContext,傳遞依賴
└──?pb
????└──?check.pb.go
go run check.go -f etc/check.yaml
可運行該服務(wù)
修改etc/check.yaml的端口為8081(因為8080已經(jīng)被add服務(wù)使用了)
再回去修改API Gateway代碼,調(diào)用add/check rpc服務(wù)
api/etc/bookstore-api.yaml,增加如下內(nèi)容
Add:
??Etcd:
????Hosts:
??????-?localhost:2379
????Key:?add.rpc
Check:
??Etcd:
????Hosts:
??????-?localhost:2379
????Key:?check.rpc
通過etcd自動去發(fā)現(xiàn)可用的add和check服務(wù)

修改api/internal/config/config.go如下,增加add&check服務(wù)依賴

修改api/internal/svc/servicecontext.go,如下:

通過ServiceContext在不同業(yè)務(wù)邏輯之間傳遞依賴
(問:怎么解決依賴注入問題)
修改api/internal/logic/addlogic.go里的Add方法,如下:
通過調(diào)用adder的Add方法實現(xiàn)添加圖書到bookstore系統(tǒng)
修改api/internal/logic/checklogic.go里的Check方法,如下:

通過調(diào)用checker的Check方法實現(xiàn)從bookstore系統(tǒng)中查詢圖書的價格
定義數(shù)據(jù)庫表結(jié)構(gòu),并生成CRUD+cache代碼
bookstore下創(chuàng)建rpc/model目錄
mkdir -p rpc/model
(不過一般習慣把這個model文件夾抽出來,和api,rpc在一層)
在rpc/model目錄下編寫創(chuàng)建book表的sql文件book.sql,如下:
CREATE?TABLE?`book`
(
??`book`?varchar(255)?NOT?NULL?COMMENT?'book?name',
??`price`?int?NOT?NULL?COMMENT?'book?price',
??PRIMARY?KEY(`book`)
)?ENGINE=InnoDB?DEFAULT?CHARSET=utf8mb4;
進入mysql命令行,創(chuàng)建DB和table
create?database?gozero;
source?book.sql;
在rpc/model目錄下執(zhí)行如下命令生成CRUD+cache代碼,-c表示使用redis cache
goctl model mysql ddl -c -src book.sql -dir .

修改add rpc和check rpc,調(diào)用crud+cache代碼
修改rpc/add/etc/add.yaml和rpc/check/etc/check.yaml,均增加如下內(nèi)容:
DataSource:?root:123456@@tcp(xxx.xxx.xx.xx:3306)/gozero
#DataSource:?root:123456@@tcp(localhost:3306)/gozero?charset=utf8mb4&parseTime=true&loc=Asia%2FShanghai?#可以這樣指定一些其他信息
Table:?book
Cache:
??-?Host:?localhost:6379

可以使用多個redis作為cache,支持redis單點或者redis集群
修改rpc/add/internal/config.go和rpc/check/internal/config.go,如下:


修改rpc/add/internal/svc/servicecontext.go和rpc/check/internal/svc/servicecontext.go,如下:


修改rpc/add/internal/logic/addlogic.go,如下


修改rpc/check/internal/logic/checklogic.go,如下:

項目使用
需要先全部啟動api服務(wù)所依賴的rpc服務(wù)。如果先啟動api,則會報錯:
error: context deadline exceeded, make sure rpc service "add.rpc" is already started
全部啟動:



(后面可以 -f指定不同環(huán)境的xxx.yaml)
調(diào)用add api,新增圖書
curl -i "http://localhost:8888/add?book=Bible&price=10"

此時看數(shù)據(jù)庫,book表里新增了一行數(shù)據(jù)
調(diào)用check api,檢查某本圖書的價格
curl -i "http://localhost:8888/check?book=Bible"


重啟check rpc,再次執(zhí)行curl -i "http://localhost:8888/check?book=Bible"

完整項目代碼[2]
參考資料
使用go-zero快速構(gòu)建微服務(wù): http://www.jikejiaocheng.com/c/gozero-microservices.html
[2]完整項目代碼: https://github.com/cuishuang/bookstore文章來源:http://www.zghlxwxcb.cn/news/detail-640421.html
本文由 mdnice 多平臺發(fā)布文章來源地址http://www.zghlxwxcb.cn/news/detail-640421.html
到了這里,關(guān)于使用go-zero快速構(gòu)建微服務(wù)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!