微服務從程式碼到k8s部署應有盡有系列(五、民宿服務)

語言: CN / TW / HK

我們用一個系列來講解從需求到上線、從程式碼到k8s部署、從日誌到監控等各個方面的微服務完整實踐。

整個專案使用了go-zero開發的微服務,基本包含了go-zero以及相關go-zero作者開發的一些中介軟體,所用到的技術棧基本是go-zero專案組的自研元件,基本是go-zero全家桶了。

實戰專案地址:https://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文件基本都知道是什麼了,根據上面例子程式碼自行檢視即可,後面有牽扯業務複雜的地方會逐一說明

專案地址

https://github.com/zeromicro/go-zero

https://gitee.com/kevwan/go-zero

歡迎使用 go-zerostar 支援我們!

微信交流群

關注『微服務實踐』公眾號並點選 交流群 獲取社群群二維碼。