微服務從程式碼到k8s部署應有盡有系列(五、民宿服務)
我們用一個系列來講解從需求到上線、從程式碼到k8s部署、從日誌到監控等各個方面的微服務完整實踐。
整個專案使用了go-zero開發的微服務,基本包含了go-zero以及相關go-zero作者開發的一些中介軟體,所用到的技術棧基本是go-zero專案組的自研元件,基本是go-zero全家桶了。
實戰專案地址:http://github.com/Mikaelemmmm/go-zero-looklook
1、民宿服務業務架構圖
2、依賴關係
travel-api(民宿api) 依賴 travel-rpc(民宿rpc)、usercenter-rpc(使用者中心rpc)
usercenter-rpc(使用者中心rpc)依賴 identity-rpc(授權中心rpc)
travel分為幾個業務
- homestay :民宿房源
// 民宿模組v1版本的介面
@server(
prefix: travel/v1
group: homestay
)
service travel {
@doc "民宿列表(為你優選)"
@handler homestayList
post /homestay/homestayList (HomestayListReq) returns (HomestayListResp)
@doc "房東所有民宿列表"
@handler businessList
post /homestay/businessList (BusinessListReq) returns (BusinessListResp)
@doc "猜你喜歡民宿列表"
@handler guessList
post /homestay/guessList (GuessListReq) returns (GuessListResp)
@doc "民宿詳情"
@handler homestayDetail
post /homestay/homestayDetail (HomestayDetailReq) returns (HomestayDetailResp)
}
- homestayBusiness : 民宿店家
// 店鋪模組v1版本的介面
@server(
prefix: travel/v1
group: homestayBussiness
)
service travel {
@doc "最佳房東"
@handler goodBoss
post /homestayBussiness/goodBoss (GoodBossReq) returns (GoodBossResp)
@doc "店鋪列表"
@handler homestayBussinessList
post /homestayBussiness/homestayBussinessList (HomestayBussinessListReq) returns (HomestayBussinessListResp)
@doc "房東資訊"
@handler homestayBussinessDetail
post /homestayBussiness/homestayBussinessDetail (HomestayBussinessDetailReq) returns (HomestayBussinessDetailResp)
}
- homestayComment : 民宿評論
// 民宿評論模組v1版本的介面
@server(
prefix: travel/v1
group: homestayComment
)
service travel {
@doc "民宿評論列表"
@handler commentList
post /homestayComment/commentList (CommentListReq) returns (CommentListResp)
}
3、舉例:民宿列表(為你優選)
1、api服務
1、寫api介面檔案
app/travel/cmd/api/desc/homestay/homestay.api
type (
HomestayListReq {
LastId int64 `json:"lastId"`
PageSize int64 `json:"pageSize"`
RowType string `json:"rowType"` //preferredHomestay:優選民宿
}
HomestayListResp {
List []Homestay `json:"list"`
}
)
app/travel/cmd/api/desc/travel.api
import (
"homestay/homestay.api"
....
)
// 民宿模組v1版本的介面
@server(
prefix: travel/v1
group: homestay
)
service travel {
@doc "民宿列表(為你優選)"
@handler homestayList
post /homestay/homestayList (HomestayListReq) returns (HomestayListResp)
......
}
2、goctl生成api程式碼
1)命令列進入app/travel/cmd/api/desc目錄下。
2)去專案目錄下deploy/script/gencode/gen.sh中,複製如下一條命令,在命令列中執行(命令列要切換到app/travel/cmd目錄)
$ goctl api go -api *.api -dir ../ -style=goZero
3、開啟app/travel/cmd/api/internal/logic/homestay/homestayListLogic.go
因為我們的推薦是在後臺配置的,所以我們建立了一個活動表(這裡你也可以選擇配置到redis中),總之我們就是先從活動表中拿到配置的推薦民宿id,然後再通過id去獲取對應民宿資訊列表。
2【小技巧】 mapreduce
這裡可以看到,我拿到了id集合之後,不是普通的foreach一個個獲取,而是使用了go-zero為我們封裝好了的mapreduce獲取資料,這樣就可以併發去獲取資料,而不是要去取一個完成之後再取下一個,時間上大大縮短了,這裡只是想給搭建展示這樣一個功能,有的同學非要較真,可以傳遞一個id slice或者id arr到rpc,然後在rpc中在去併發獲取每個,這樣也沒什麼不好,我這裡只是給大家展示這個功能
3、rpc服務
定義protobuf檔案
app/travel/cmd/rpc/pb/travel.proto
// model
message Homestay {
int64 id = 1;
string title = 2;
string subTitle = 3;
string banner = 4;
string info = 5;
int64 peopleNum = 6; // 容納人的數量
int64 homestayBusinessId = 7; // 店鋪id
int64 userId = 8; // 房東id
int64 rowState = 9; // 0:下架 1:上架
int64 rowType = 10; // 售賣型別0:按房間出售 1:按人次出售
string foodInfo = 11; // 餐食標準
int64 foodPrice = 12; // 餐食價格(分)
int64 homestayPrice = 13; // 民宿價格(分)
int64 marketHomestayPrice = 14; // 民宿市場價格(分)
}
// req 、resp
message HomestayDetailReq {
int64 id = 1;
}
message HomestayDetailResp {
Homestay homestay = 1;
}
// service
service travel {
// 民宿詳情
rpc homestayDetail(HomestayDetailReq) returns(HomestayDetailResp);
}
-
使用goctl生成程式碼,這裡不需要自己手動敲
1)命令列進入app/travel/cmd/rpc/pb目錄下。
2)去專案目錄下deploy/script/gencode/gen.sh中,複製如下兩條命令,在命令列中執行(命令列要切換到app/travel/cmd目錄)
$ goctl rpc protoc *.proto --go_out=../ --go-grpc_out=../ --zrpc_out=../ $ sed -i "" 's/,omitempty//g' *.pb.go
-
開啟app/travel/cmd/rpc/internal/logic/homestayDetailLogic.go寫邏輯程式碼
這裡沒什麼邏輯,查詢Findone,然後返回給api,因為api那邊是通過id傳遞過來的,然後可以看到我們這邊又一次使用了前一章提到的gorm作者提供的另外一款神器copier,上一節是在api中使用,將rpc的proto檔案的資料copy到api檔案 , 這裡可以看到,我們把model返回的資料copy給proto的資料同樣可以用,怎麼樣是不是很方便。
4、【小技巧】 model cache、singleflight
在這裡為什麼我們不去findlist,是因為我們在findone方法中有快取,我們一個個根據id查詢資料時候,只有第一次會命中db,其他時間基本都是命中的redis cache,這樣不僅速度快,就算流量激增的時候,也不會全部打到db上,而是都在redis上,這樣會大大提高我們系統的訪問速度以及db支撐能力。
一般我們自己維護db cache會寫的零零散散,但是go-zero使用了配套內建工具goctl生成的model,自帶sqlc+sqlx實現的程式碼,實現了自動快取管理,我們根本不需要去管理快取,只需要用sqlx寫 sql資料,sqlc會自動幫我們管理快取,並且是通過singleflight ,也就是說即使快取在某個時間失效,在失效那一刻同時有大量併發請求進來時,go-zero在查詢db時候也只會放行一個執行緒進來,其他執行緒是在等待,當這個執行緒從資料庫拿資料回來之後將該資料快取到redis同時所有之前等待執行緒共享此資料返回,後續在進來的執行緒查相同資料時,就只會進入到redis中而不會進入到db。
這樣rpc拿到所有資料之後,就可以返回給前端顯示了。
4、小結
其他的幾個服務沒有業務什麼邏輯性的這裡就不再一一說明,看api文件基本都知道是什麼了,根據上面例子程式碼自行檢視即可,後面有牽扯業務複雜的地方會逐一說明
專案地址
http://github.com/zeromicro/go-zero
http://gitee.com/kevwan/go-zero
歡迎使用 go-zero
並 star 支援我們!
微信交流群
關注『微服務實踐』公眾號並點選 交流群 獲取社群群二維碼。
- go-zero微服務實戰系列(八、如何處理每秒上萬次的下單請求)
- go-zero微服務實戰系列(七、請求量這麼高該如何優化)
- go-zero微服務實戰系列(六、快取一致性保證)
- 詳解連線池引數設定(邊調邊看)
- go-zero微服務實戰系列(五、快取程式碼怎麼寫)
- go-zero微服務實戰系列(四、CRUD熱熱身)
- go-zero微服務實戰系列(三、API定義和表結構設計)
- go-zero 微服務實戰系列(二、服務拆分)
- go-zero 微服務實戰系列(一、開篇)
- 微服務效率工具 goctl 深度解析(上)
- 型別安全的 Go HTTP 請求
- 用 Go 快速開發一個 RESTful API 服務
- Go 專案配置檔案的定義和讀取
- 簡單易懂的 Go 泛型使用和實現原理介紹
- Go單體服務開發最佳實踐
- 通過 SingleFlight 模式學習 Go 併發程式設計
- 程序內優雅管理多個服務
- 構建 Go 應用 docker 映象的十八種姿勢
- 史上最強程式碼自測方法,沒有之一!
- 微服務從程式碼到k8s部署應有盡有大結局(k8s部署)