微服務(wù)從代碼到k8s部署應(yīng)有盡有系列(五、民宿服務(wù))
我們用一個(gè)系列來(lái)講解從需求到上線、從代碼到k8s部署、從日志到監(jiān)控等各個(gè)方面的微服務(wù)完整實(shí)踐。
整個(gè)項(xiàng)目使用了go-zero開(kāi)發(fā)的微服務(wù),基本包含了go-zero以及相關(guān)go-zero作者開(kāi)發(fā)的一些中間件,所用到的技術(shù)棧基本是go-zero項(xiàng)目組的自研組件,基本是go-zero全家桶了。
實(shí)戰(zhàn)項(xiàng)目地址:https://github.com/Mikaelemmmm/go-zero-looklook
1、民宿服務(wù)業(yè)務(wù)架構(gòu)圖

2、依賴關(guān)系
travel-api(民宿api) 依賴 travel-rpc(民宿r(nóng)pc)、usercenter-rpc(用戶中心rpc)
usercenter-rpc(用戶中心rpc)依賴 identity-rpc(授權(quán)中心rpc)
travel分為幾個(gè)業(yè)務(wù)
homestay :民宿房源
// 民宿模塊v1版本的接口
@server(
prefix: travel/v1
group: homestay
)
service travel {
@doc "民宿列表(為你優(yōu)選)"
@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 :民宿評(píng)論
// 民宿評(píng)論模塊v1版本的接口
@server(
prefix: travel/v1
group: homestayComment
)
service travel {
@doc "民宿評(píng)論列表"
@handler commentList
post /homestayComment/commentList (CommentListReq) returns (CommentListResp)
}
3、舉例:民宿列表(為你優(yōu)選)
1、api服務(wù)
1、寫(xiě)api接口文件
app/travel/cmd/api/desc/homestay/homestay.api
type (
HomestayListReq {
LastId int64 `json:"lastId"`
PageSize int64 `json:"pageSize"`
RowType string `json:"rowType"` //preferredHomestay:優(yōu)選民宿
}
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 "民宿列表(為你優(yōu)選)"
@handler homestayList
post /homestay/homestayList (HomestayListReq) returns (HomestayListResp)
......
}
2、goctl生成api代碼
1)命令行進(jìn)入app/travel/cmd/api/desc目錄下。
2)去項(xiàng)目目錄下deploy/script/gencode/gen.sh中,復(fù)制如下一條命令,在命令行中執(zhí)行(命令行要切換到app/travel/cmd目錄)
$?goctl?api?go?-api?*.api?-dir?../??-style=goZero
3、打開(kāi)app/travel/cmd/api/internal/logic/homestay/homestayListLogic.go

因?yàn)槲覀兊耐扑]是在后臺(tái)配置的,所以我們創(chuàng)建了一個(gè)活動(dòng)表(這里你也可以選擇配置到redis中),總之我們就是先從活動(dòng)表中拿到配置的推薦民宿id,然后再通過(guò)id去獲取對(duì)應(yīng)民宿信息列表。
2【小技巧】 mapreduce
這里可以看到,我拿到了id集合之后,不是普通的foreach一個(gè)個(gè)獲取,而是使用了go-zero為我們封裝好了的mapreduce獲取數(shù)據(jù),這樣就可以并發(fā)去獲取數(shù)據(jù),而不是要去取一個(gè)完成之后再取下一個(gè),時(shí)間上大大縮短了,這里只是想給搭建展示這樣一個(gè)功能,有的同學(xué)非要較真,可以傳遞一個(gè)id slice或者id arr到rpc,然后在rpc中在去并發(fā)獲取每個(gè),這樣也沒(méi)什么不好,我這里只是給大家展示這個(gè)功能
3、rpc服務(wù)
定義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; // 容納人的數(shù)量
int64 homestayBusinessId = 7; // 店鋪id
int64 userId = 8; // 房東id
int64 rowState = 9; // 0:下架 1:上架
int64 rowType = 10; // 售賣(mài)類(lèi)型0:按房間出售 1:按人次出售
string foodInfo = 11; // 餐食標(biāo)準(zhǔn)
int64 foodPrice = 12; // 餐食價(jià)格(分)
int64 homestayPrice = 13; // 民宿價(jià)格(分)
int64 marketHomestayPrice = 14; // 民宿市場(chǎng)價(jià)格(分)
}
// req 、resp
message HomestayDetailReq {
int64 id = 1;
}
message HomestayDetailResp {
Homestay homestay = 1;
}
// service
service travel {
// 民宿詳情
rpc homestayDetail(HomestayDetailReq) returns(HomestayDetailResp);
}
使用goctl生成代碼,這里不需要自己手動(dòng)敲
1)命令行進(jìn)入app/travel/cmd/rpc/pb目錄下。
2)去項(xiàng)目目錄下deploy/script/gencode/gen.sh中,復(fù)制如下兩條命令,在命令行中執(zhí)行(命令行要切換到app/travel/cmd目錄)
$?goctl?rpc?protoc?*.proto?--go_out=../?--go-grpc_out=../??--zrpc_out=../
$?sed?-i?""?'s/,omitempty//g'?*.pb.go打開(kāi)app/travel/cmd/rpc/internal/logic/homestayDetailLogic.go寫(xiě)邏輯代碼

這里沒(méi)什么邏輯,查詢Findone,然后返回給api,因?yàn)閍pi那邊是通過(guò)id傳遞過(guò)來(lái)的,然后可以看到我們這邊又一次使用了前一章提到的gorm作者提供的另外一款神器copier,上一節(jié)是在api中使用,將rpc的proto文件的數(shù)據(jù)copy到api文件 , 這里可以看到,我們把model返回的數(shù)據(jù)copy給proto的數(shù)據(jù)同樣可以用,怎么樣是不是很方便。
4、【小技巧】 model cache、singleflight
在這里為什么我們不去findlist,是因?yàn)槲覀冊(cè)趂indone方法中有緩存,我們一個(gè)個(gè)根據(jù)id查詢數(shù)據(jù)時(shí)候,只有第一次會(huì)命中db,其他時(shí)間基本都是命中的redis cache,這樣不僅速度快,就算流量激增的時(shí)候,也不會(huì)全部打到db上,而是都在redis上,這樣會(huì)大大提高我們系統(tǒng)的訪問(wèn)速度以及db支撐能力。
一般我們自己維護(hù)db cache會(huì)寫(xiě)的零零散散,但是go-zero使用了配套內(nèi)置工具goctl生成的model,自帶sqlc+sqlx實(shí)現(xiàn)的代碼,實(shí)現(xiàn)了自動(dòng)緩存管理,我們根本不需要去管理緩存,只需要用sqlx寫(xiě) sql數(shù)據(jù),sqlc會(huì)自動(dòng)幫我們管理緩存,并且是通過(guò)singleflight ,也就是說(shuō)即使緩存在某個(gè)時(shí)間失效,在失效那一刻同時(shí)有大量并發(fā)請(qǐng)求進(jìn)來(lái)時(shí),go-zero在查詢db時(shí)候也只會(huì)放行一個(gè)線程進(jìn)來(lái),其他線程是在等待,當(dāng)這個(gè)線程從數(shù)據(jù)庫(kù)拿數(shù)據(jù)回來(lái)之后將該數(shù)據(jù)緩存到redis同時(shí)所有之前等待線程共享此數(shù)據(jù)返回,后續(xù)在進(jìn)來(lái)的線程查相同數(shù)據(jù)時(shí),就只會(huì)進(jìn)入到redis中而不會(huì)進(jìn)入到db。
這樣rpc拿到所有數(shù)據(jù)之后,就可以返回給前端顯示了。
4、小結(jié)
其他的幾個(gè)服務(wù)沒(méi)有業(yè)務(wù)什么邏輯性的這里就不再一一說(shuō)明,看api文檔基本都知道是什么了,根據(jù)上面例子代碼自行查看即可,后面有牽扯業(yè)務(wù)復(fù)雜的地方會(huì)逐一說(shuō)明
項(xiàng)目地址
https://github.com/zeromicro/go-zero
歡迎使用 go-zero 并 star 支持我們!
推薦閱讀
