Golang 語(yǔ)言編寫(xiě) gRPC 實(shí)戰(zhàn)項(xiàng)目
1、介紹
在之前的幾篇文章中,我們介紹了 protobuf 和 grpc,本文我們介紹怎么使用 grpc 開(kāi)發(fā)“分布式系統(tǒng)”。這里使用引號(hào)是因?yàn)榉植际较到y(tǒng)是一個(gè)大概念,本文我們先介紹使用 grpc 開(kāi)發(fā)分布式系統(tǒng)中的 service。
grpc 是 google 開(kāi)源的 rpc 框架,使用 grpc 可以方便開(kāi)發(fā) rpc service;protobuf 是一種接口設(shè)計(jì)語(yǔ)言(IDL),grpc 框架使用的 IDL 是 protobuf。如果有讀者朋友還不了解 protobuf 和 grpc,建議先翻閱之前的幾篇文章。
本文是介紹使用 grpc 開(kāi)發(fā)一個(gè)實(shí)戰(zhàn)項(xiàng)目 - ToDoList,目標(biāo)是幫助讀者朋友們熟悉項(xiàng)目開(kāi)發(fā)流程,該實(shí)戰(zhàn)項(xiàng)目包含 server service 和 client service。server 主要負(fù)責(zé)數(shù)據(jù)操作,client 主要負(fù)責(zé)業(yè)務(wù)邏輯處理。
2、server
首先,我們創(chuàng)建 proto 目錄,并創(chuàng)建 proto 文件,編寫(xiě) protobuf,設(shè)計(jì)項(xiàng)目的 service,接著創(chuàng)建 pb 目錄,使用 protoc 編譯我們編寫(xiě)好的 proto 文件,生成 pb 文件。然后,我們創(chuàng)建 service 目錄,編寫(xiě)生成的 pb 文件中接口定義的方法。最后,我們創(chuàng)建 grpc 服務(wù)器。
server 目錄
- .
- ├── dao
- │ ├── mysql.go
- │ └── toDoList.go
- ├── main.go
- ├── pb
- │ ├── todoPb
- │ │ ├── toDoList.pb.go
- │ │ └── toDoList_grpc.pb.go
- │ └── userPb
- │ ├── user.pb.go
- │ └── user_grpc.pb.go
- ├── proto
- │ ├── toDoList.proto
- │ └── user.proto
- └── service
- └── toDoList.go
編寫(xiě) proto 文件
讀者朋友們?nèi)绻€不熟悉 protobuf,建議翻閱之前介紹 protobuf 的文章,限于篇幅,本文不再贅述。示例代碼如下:
- syntax = "proto3";
- option go_package = "./todoPb";
- service ToDoList {
- rpc CreateToDoList (ToDoListDetail) returns (CreateToDoListResult) {}
- rpc ReadToDoList (ToDoListPage) returns (ReadToDoListByPage) {}
- }
- message ToDoListDetail {
- // @inject_tag: form:"id" xorm:"'id' not null pk autoincr"
- int64 id = 1;
- ...
完整代碼,請(qǐng)查閱 github。
生成 pb 文件
接著,我們使用 protoc 編譯 proto 文件,生成 pb 文件,關(guān)于怎么使用 protoc 編譯 proto 文件,在之前的文章已經(jīng)詳細(xì)介紹,限于篇幅,本文不再贅述,編譯命令如下:
- protoc --go_out=./pb --go-grpc_out=./pb proto/* && protoc-go-inject-tag -XXX_skip=xorm -input=./pb/todoPb/toDoList.pb.go
執(zhí)行以上命令,將在 pb 目錄中自動(dòng)生成 pb 文件。
編寫(xiě)接口定義的方法
至此,我們開(kāi)始編寫(xiě) golang 代碼,在 service 目錄中創(chuàng)建 go 文件,實(shí)現(xiàn)生成的 pb 文件中接口定義的方法。
- ...
- type ToDoList struct {
- pb.UnimplementedToDoListServer
- }
- func (t *ToDoList) CreateToDoList(ctx context.Context, in *pb.ToDoListDetail) (*pb.CreateToDoListResult, error) {
- log.Printf("id: %d content:%v datetime:%d\n", in.GetId(), in.GetContent(), in.GetDatetime())
- record, err := dao.Add(ctx, in)
- data := &pb.CreateToDoListResult{Record: record}
- return data, err
- }
- ...
閱讀上面這段代碼,可以發(fā)現(xiàn)我們把數(shù)據(jù)庫(kù)操作相關(guān)代碼設(shè)計(jì)在 dao 包中。service 中通過(guò)調(diào)用 dao 包的方法操作數(shù)據(jù)庫(kù),另外,其他數(shù)據(jù)操作組件也可以在 service 中調(diào)用。
完整代碼,請(qǐng)查閱 github。
創(chuàng)建 gRPC 服務(wù)器
在完成 service 代碼編寫(xiě)之后,我們創(chuàng)建 grpc server,然后注冊(cè)服務(wù)。
- ...
- server := grpc.NewServer()
- pb.RegisterToDoListServer(server, new(service.ToDoList))
- ...
完整代碼,請(qǐng)查閱 github。
以上就是使用 grpc 創(chuàng)建 rpc service 的一般流程,在生產(chǎn)環(huán)境項(xiàng)目中,還需要完善一些公共方法,比如配置文件讀取、錯(cuò)誤碼定義、參數(shù)驗(yàn)證等。為了讀者朋友們?nèi)菀桌斫?,該?shí)戰(zhàn)項(xiàng)目中未涉及這部分內(nèi)容,感興趣的讀者朋友們可以嘗試自己實(shí)現(xiàn)該部分內(nèi)容。
3、client
client 主要負(fù)責(zé)業(yè)務(wù)邏輯,本文介紹的實(shí)戰(zhàn)項(xiàng)目使用 gin 框架實(shí)現(xiàn)路由。通常,client service 的 pb 文件拷貝 server service 生成的 pb 文件。
首先,我們創(chuàng)建 controller 目錄,調(diào)用 server service 的方法,然后,使用 gin 框架設(shè)計(jì)路由。
client 目錄
- .
- ├── controller
- │ └── toDoList.go
- ├── main.go
- ├── pb
- │ ├── todoPb
- │ │ ├── toDoList.pb.go
- │ │ └── toDoList_grpc.pb.go
- │ └── userPb
- │ ├── user.pb.go
- │ └── user_grpc.pb.go
- └── router
- └── router.go
拷貝 server service 生成的 pb 文件
client 直接拷貝 server service 生成的 pb 文件,不需要編寫(xiě) proto 文件,然后使用 protoc 編譯 proto 文件,生成 pb 文件。
編寫(xiě) controller 代碼,調(diào)用 server service 的方法
在 controller 目錄中創(chuàng)建 go 文件,編寫(xiě) controller 方法,并創(chuàng)建客戶端,使用創(chuàng)建的客戶端調(diào)用 server service 的方法。
- func CreateToDoList(ctx *gin.Context) {
- ...
- cc := NewToDoListClient()
- defer func() {
- err := cc.Close()
- if err != nil {
- log.Fatalf("conn close error=%v", err)
- }
- }()
- cli := pb.NewToDoListClient(cc)
- ctx1, cancel := context.WithTimeout(context.Background(), time.Second)
- defer cancel()
- res, err := cli.CreateToDoList(ctx1, param)
- ...
完整代碼,請(qǐng)查閱 github。
創(chuàng)建 gin 路由
編寫(xiě)完 controller 之后,創(chuàng)建 router 目錄,在 router 目錄中創(chuàng)建 gin 路由,用于訪問(wèn) controller 中的方法。
- ...
- r := gin.Default()
- apiV1 := r.Group("/v1")
- todolist := apiV1.Group("/todolist")
- {
- todolist.POST("/add", controller.CreateToDoList)
- ...
完整代碼,請(qǐng)查閱 github。
4、總結(jié)
本文我們介紹了怎么使用 grpc 開(kāi)發(fā) service。讀者朋友們閱讀完本文,可以了解使用 grpc 開(kāi)發(fā) service 的一般開(kāi)發(fā)流程,建議感興趣的讀者朋友們,實(shí)現(xiàn)項(xiàng)目中 user service 的代碼編寫(xiě)。