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

我們用一個系列來講解從需求到上線、從代碼到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

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

微信交流羣

關注『微服務實踐』公衆號並點擊 交流羣 獲取社區羣二維碼。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章