Categories
程式開發

你還在手撕微服務?快試試go-zero 的微服務自動生成


0. 為什麼說做好微服務很難?

要想做好微服務,我們需要理解和掌握的知識點非常多,從幾個維度上來說:

基本功能層面

1. 並發控制&限流,避免服務被突發流量擊垮

2. 服務註冊與服務發現,確保能夠動態偵測增減的節點

3. 負載均衡,需要根據節點承受能力分發流量

4. 超時控制,避免對已超時請求做無用功

5. 熔斷設計,快速失敗,保障故障節點的恢復能力

高階功能層面

1. 請求認證,確保每個用戶只能訪問自己的數據

2. 鏈路追踪,用於理解整個系統和快速定位特定請求的問題

3. 日誌,用於數據收集和問題定位

4. 可觀測性,沒有度量就沒有優化

對於其中每一點,我們都需要用很長的篇幅來講述其原理和實現,那麼對我們後端開發者來說,要想把這些知識點都掌握並落實到業務系統裡,難度是非常大的,不過我們可以依賴已經被大流量驗證過的框架體系。go-zero微服務框架“就是為此而生。

另外,我們始終秉承工具大於約定和文檔的理念。我們希望盡可能減少開發人員的心智負擔,把精力都投入到產生業務價值的代碼上,減少重複代碼的編寫,所以我們開發了goctl工具。

下面我通過短鏈微服務來演示通過歸零“快速的創建微服務的流程,走完一遍,你就會發現:原來編寫微服務如此簡單!

1. 什麼是短鏈服務?

短鏈服務就是將長的URL網址,通過程序計算等方式,轉換為簡短的網址字符串。

寫此短鏈服務是為了從整體上演示go-zero構建完整微服務的過程,算法和實現細節盡可能簡化了,所以這不是一個高階的短鏈服務。

2. 短鏈微服務架構圖

你還在手撕微服務?快試試go-zero 的微服務自動生成 1

這裡只用了Transform RPC一個微服務,並不是說API Gateway只能調用一個微服務,只是為了最簡演示API Gateway如何調用RPC微服務而已在真正項目裡要盡可能每個微服務使用自己的數據庫,數據邊界要清晰

3. goctl各層代碼生成一覽

所有綠色背景的功能模塊是自動生成的,按需激活,紅色模塊是需要自己寫的,也就是增加下依賴,編寫業務特有邏輯,各層示意圖分別如下:

API網關

你還在手撕微服務?快試試go-zero 的微服務自動生成 2

RPC

你還在手撕微服務?快試試go-zero 的微服務自動生成 3

模型

你還在手撕微服務?快試試go-zero 的微服務自動生成 4

下面我們來一起完整走一遍快速構建微服務的流程,Let’s Go!🏃‍♂️

4. 準備工作

安裝etcd, mysql, redis

安裝goctl工具

外殼

GO111MODULE =在GOPROXY上= https://goproxy.cn/,直接轉到github.com/tal-tech/go-zero/tools/goctl

“`

創建工作目錄shorturl

在shorturl目錄下執行go mod init shorturl初始化go.mod

5. 編寫API Gateway代碼

通過goctl生成api/shorturl.api並編輯,為了簡潔,去除了文件開頭的info,代碼如下:

類型(

expandReq結構{

縮短字符串形式:“縮短”

}

expandResp struct {

url字符串json:“ url”

}

類型(

ShortReq結構{

url字符串形式:“ url”

}

shortResp struct {

縮短字符串json:“縮短”

}

服務shorturl-api {

@服務器(

處理程序:ShortenHandler

獲取/縮短(shortenReq)返回(shortenResp)

@服務器(

處理程序:ExpandHandler

獲取/ expand(expandReq)返回(expandResp)

}

“`

type用法和go一致,service用來定義get/post/head/delete等api請求,解釋如下:

* service shorturl-api {這一行定義了service名字

* @server部分用來定義server端用到的屬性

* handler定義了服務端handler名字

* get /shorten(shortenReq) returns(shortenResp)定義了get方法的路由、請求參數、返回參數等

使用goctl生成API Gateway代碼

外殼

goctl api go -api shorturl.api -dir。

“`

生成的文件結構如下:

“`

├──API

│├──等

│ │ └── shorturl-api.yaml // 配置文件

│├──內部

││├──配置

│ │ │ └── config.go // 定義配置

││├──搬運工

│││├──expandhandler.go //實現expandHandler

│ │ │ ├── routes.go // 定義路由處理

││││──shorthandhandler.go//實現shortenHandler

││├──邏輯

│ │ │ ├── expandlogic.go // 實現ExpandLogic

│││└──shorticlogic.go//實現ShortenLogic

││├──svc

│ │ │ └── servicecontext.go // 定義ServiceContext

││└──類型

│ │ └── types.go // 定義請求、返回結構體

│├──shorturl.api

│ └── shorturl.go // main入口定義

├──go.mod

└──go.sum

“`

啟動API Gateway服務,默認偵聽在8888端口

外殼

去運行shorturl.go -f etc / shorturl-api.yaml

“`

測試API Gateway服務

外殼

curl -i“ http:// localhost:8888 / shorten?url = http://www.xiaoheiban.cn”

“`

返回如下:

“`http

HTTP / 1.1 200 OK

內容類型:application / json

日期:格林尼治標準時間2020年8月27日星期四14:31:39

內容長度:15

{“ shortUrl”:“”}

“`

可以看到我們API Gateway其實啥也沒幹,就返回了個空值,接下來我們會在rpc服務裡實現業務邏輯

可以修改internal/svc/servicecontext.go來傳遞服務依賴(如果需要)

實現邏輯可以修改internal/logic下的對應文件

可以通過goctl生成各種客戶端語言的api調用代碼

到這裡,你已經可以通過goctl生成客戶端代碼給客戶端同學並行開發了,支持多種語言,詳見文檔

6. 編寫transform rpc服務

在rpc/transform目錄下編寫transform.proto文件

可以通過命令生成proto文件模板

外殼

goctl rpc模板-o transform.proto

“`

修改後文件內容如下:

“protobuf

語法=“ proto3”;

打包轉換;

消息expandReq {

字符串縮短= 1;

}

消息expandResp {

字符串url = 1;

}

消息shorteningReq {

字符串url = 1;

}

短信shortResp {

字符串縮短= 1;

}

服務變壓器{

rpc expand(expandReq)返回(expandResp);

rpc short(shortenReq)返回(shortenResp);

}

“`

用goctl生成rpc代碼,在rpc/transform目錄下執行命令

外殼

goctl rpc proto -src transform.proto

“`

文件結構如下:

“`

rpc /轉換

├──等

│ └── transform.yaml // 配置文件

├──內部

│├──配置

│ │ └── config.go // 配置定義

│├──邏輯

│ │ ├── expandlogic.go // expand業務邏輯在這裡實現

│ │ └── shortenlogic.go // shorten業務邏輯在這裡實現

│├──服務器

│ │ └── transformerserver.go // 調用入口, 不需要修改

│└──svc

│ └── servicecontext.go // 定義ServiceContext,傳遞依賴

├──鉛

│└──transform.pb.go

├── transform.go // rpc服務main函數

├──transform.proto

└──變壓器

├── transformer.go // 提供了外部調用方法,無需修改

├── transformer_mock.go // mock方法,測試用

└── types.go // request/response結構體定義

“`

直接可以運行,如下:

外殼

$去運行transform.go -f etc / transform.yaml

正在127.0.0.1:8080啟動rpc服務器…

“`

etc/transform.yaml文件裡可以修改偵聽端口等配置

7. 修改API Gateway代碼調用transform rpc服務

修改配置文件shorturl-api.yaml,增加如下內容

“`yaml

轉變:

等等

主持人:

-本地主機:2379

密鑰:transform.rpc

“`

通過etcd自動去發現可用的transform服務

修改internal/config/config.go如下,增加transform服務依賴

輸入Config struct {

休息

Transform rpcx.RpcClientConf // 手動代碼

}

“`

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

輸入ServiceContext struct {

配置config.Config

Transformer rpcx.Client // 手動代碼

}

func NewServiceContext(c config.Config)* ServiceContext {

返回&ServiceContext {

配置:c,

Transformer: rpcx.MustNewClient(c.Transform), // 手動代碼

}

}

“`

通過ServiceContext在不同業務邏輯之間傳遞依賴

修改internal/logic/expandlogic.go裡的Expand方法,如下:

func(l ExpandLogic)Expand(req types.ExpandReq)(types.ExpandResp,錯誤){

// 手動代碼開始

反式:= Translator.NewTransformer(l.svcCtx.Transformer)

resp,err:= trans.Expand(l.ctx,&transformer.ExpandReq {

縮短:要求縮短

})

如果err!= nil {

返回nil,err

}

返回&types.ExpandResp {

網址:分別為網址,

},無

// 手動代碼結束

}

“`

通過調用transformer的Expand方法實現短鏈恢復到url

修改internal/logic/shortenlogic.go,如下:

func(l ShortenLogic)縮短(req types.ShortenReq)(types.ShortenResp,錯誤){

// 手動代碼開始

反式:= Translator.NewTransformer(l.svcCtx.Transformer)

resp,err:= trans.Shorten(l.ctx,&transformer.ShortenReq {

網址:req.Url,

})

如果err!= nil {

返回nil,err

}

返回&types.ShortenResp {

縮短:分別為收縮,

},無

// 手動代碼結束

}

“`

通過調用transformer的Shorten方法實現url到短鏈的變換

至此,API Gateway修改完成,雖然貼的代碼多,但是期中修改的是很少的一部分,為了方便理解上下文,我貼了完整代碼,接下來處理CRUD+cache

8. 定義數據庫表結構,並生成CRUD+cache代碼

shorturl下創建rpc/transform/model目錄:mkdir -p rpc/transform/model

在rpc/transform/model目錄下編寫創建shorturl表的sql文件shorturl.sql,如下:

SQL

創建表shorturl

Short varchar(255)NOT NULL COMMENT’短鍵’,

url varchar(255)NOT NULL COMMENT’原始URL’,

主鍵(縮短)

)ENGINE = InnoDB DEFAULT CHARSET = utf8mb4;

“`

創建DB和table

SQL

創建數據庫gozero;

“`

SQL

源shorturl.sql;

“`

在rpc/transform/model目錄下執行如下命令生成CRUD+cache代碼,-c表示使用redis cache

外殼

goctl模型mysql ddl -c -src shorturl.sql -dir。

“`

也可以用datasource命令代替ddl來指定數據庫鏈接直接從schema生成

生成後的文件結構如下:

“`

rpc /轉換/模型

├──shorturl.sql

├── shorturlmodel.go // CRUD+cache代碼

└── vars.go // 定義常量和變量

“`

9. 修改shorten/expand rpc代碼調用crud+cache代碼

修改rpc/transform/etc/transform.yaml,增加如下內容:

“`yaml

數據源:root:@tcp(localhost:3306)/ gozero

表:shorturl

快取:

-主機:localhost:6379

“`

可以使用多個redis作為cache,支持redis單點或者redis集群

修改rpc/transform/internal/config.go,如下:

輸入Config struct {

rpcx.RpcServerConf

DataSource string // 手動代碼

Table string // 手動代碼

Cache cache.CacheConf // 手動代碼

}

“`

增加了mysql和redis cache配置

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

輸入ServiceContext struct {

c配置文件

Model *model.ShorturlModel // 手動代碼

}

func NewServiceContext(c config.Config)* ServiceContext {

返回&ServiceContext {

c:c

Model: model.NewShorturlModel(sqlx.NewMysql(c.DataSource), c.Cache, c.Table), // 手動代碼

}

}

“`

修改rpc/transform/internal/logic/expandlogic.go,如下:

func(l ExpandLogic)Expand(in expand.ExpandReq)(* expand.ExpandResp,error){

// 手動代碼開始

res,err:= l.svcCtx.Model.FindOne(in.Shorten)

如果err!= nil {

返回nil,err

}

返回&transform.ExpandResp {

網址:res.Url,

},無

// 手動代碼結束

}

“`

修改rpc/shorten/internal/logic/shortenlogic.go,如下:

func(l ShortenLogic)縮短(以short.ShortenReq為單位)(* shorten.ShortenResp,錯誤){

// 手動代碼開始,生成短鏈接

鍵:= hash.Md5Hex([]字節(in.Url))[:6]

_,err:= l.svcCtx.Model.Insert(model.Shorturl {

縮短:鍵,

網址:in.Url,

})

如果err!= nil {

返回nil,err

}

返回&transform.ShortenResp {

縮短:鍵,

},無

// 手動代碼結束

}

“`

至此代碼修改完成,凡事手動修改的代碼我加了標註

10. 完整調用演示

shorten api調用

外殼

curl -i“ http:// localhost:8888 / shorten?url = http://www.xiaoheiban.cn”

“`

返回如下:

“`http

HTTP / 1.1 200 OK

內容類型:application / json

日期:2020年8月29日,星期六,格林尼治標準時間

內容長度:21

{“縮短”:“ f35b2a”}

“`

expand api調用

外殼

curl -i“ http:// localhost:8888 / expand?shorten = f35b2a”

“`

返回如下:

“`http

HTTP / 1.1 200 OK

內容類型:application / json

日期:2020年8月29日,星期六10:51:53 GMT

內容長度:34

{“ url”:“ http://www.xiaoheiban.cn”}

“`

11.基準

因為寫入依賴於mysql的寫入速度,就相當於壓mysql了,所以壓測只測試了expand接口,相當於從mysql裡讀取並利用緩存,shorten.lua裡隨機從db裡獲取了100個熱key來生成壓測請求

你還在手撕微服務?快試試go-zero 的微服務自動生成 5

可以看出在我的MacBook Pro上能達到3萬+的qps。

12. 總結

我們一直強調工具大於約定和文檔。

go-zero不只是一個框架,更是一個建立在框架+工具基礎上的,簡化和規範了整個微服務構建的技術體系。

我們在保持簡單的同時也盡可能把微服務治理的複雜度封裝到了框架內部,極大的降低了開發人員的心智負擔,使得業務開發得以快速推進。

通過go-zero+goctl生成的代碼,包含了微服務治理的各種組件,包括:並發控制、自適應熔斷、自適應降載、自動緩存控制等,可以輕鬆部署以承載巨大訪問量。

有任何好的提升工程效率的想法,隨時歡迎交流! 👏

13. 項目地址

https://github.com/tal-tech/go-zero