帶你十天輕松搞定 Go 微服務系列(四)
序言
我們通過一個系列文章跟大家詳細展示一個 go-zero 微服務示例,整個系列分十篇文章,目錄結構如下:
環(huán)境搭建:帶你十天輕松搞定 Go 微服務系列(一) 服務拆分:帶你十天輕松搞定 Go 微服務系列(二) 用戶服務:帶你十天輕松搞定 Go 微服務系列(三) 產(chǎn)品服務(本文) 訂單服務 支付服務 RPC 服務 Auth 驗證 服務監(jiān)控 鏈路追蹤 分布式事務
期望通過本系列帶你在本機利用 Docker 環(huán)境利用 go-zero 快速開發(fā)一個商城系統(tǒng),讓你快速上手微服務。
完整示例代碼:https://github.com/nivin-studio/go-zero-mall
首先,我們來看一下整體的服務拆分圖:

4. 產(chǎn)品服務(product)
進入服務工作區(qū)
$?cd?mall/service/product
4.1 生成 product model 模型
創(chuàng)建 sql 文件
$?vim?model/product.sql
編寫 sql 文件
CREATE?TABLE?`product`?(
?`id`?bigint?unsigned?NOT?NULL?AUTO_INCREMENT,
?`name`?varchar(255)??NOT?NULL?DEFAULT?''?COMMENT?'產(chǎn)品名稱',
?`desc`?varchar(255)??NOT?NULL?DEFAULT?''?COMMENT?'產(chǎn)品描述',
????????`stock`?int(10)?unsigned?NOT?NULL?DEFAULT?'0'?COMMENT?'產(chǎn)品庫存',
?`amount`?int(10)?unsigned?NOT?NULL?DEFAULT?'0'??COMMENT?'產(chǎn)品金額',
?`status`?tinyint(3)?unsigned?NOT?NULL?DEFAULT?'0'?COMMENT?'產(chǎn)品狀態(tài)',
?`create_time`?timestamp?NULL?DEFAULT?CURRENT_TIMESTAMP,
?`update_time`?timestamp?NULL?DEFAULT?CURRENT_TIMESTAMP?ON?UPDATE?CURRENT_TIMESTAMP,
?PRIMARY?KEY?(`id`)
)?ENGINE=InnoDB??DEFAULT?CHARSET=utf8mb4;
運行模板生成命令
$?goctl?model?mysql?ddl?-src?./model/product.sql?-dir?./model?-c
4.2 生成 product api 服務
創(chuàng)建 api 文件
$?vim?api/product.api
編寫 api 文件
type?(
?//?產(chǎn)品創(chuàng)建
?CreateRequest?{
??Name???string?`json:"name"`
??Desc???string?`json:"desc"`
??Stock??int64??`json:"stock"`
??Amount?int64??`json:"amount"`
??Status?int64??`json:"status"`
?}
?CreateResponse?{
??Id?int64?`json:"id"`
?}
?//?產(chǎn)品創(chuàng)建
?//?產(chǎn)品修改
?UpdateRequest?{
??Id?????int64??`json:"id"`
??Name???string?`json:"name,optional"`
??Desc???string?`json:"desc,optional"`
??Stock??int64??`json:"stock"`
??Amount?int64??`json:"amount,optional"`
??Status?int64??`json:"status,optional"`
?}
?UpdateResponse?{
?}
?//?產(chǎn)品修改
?//?產(chǎn)品刪除
?RemoveRequest?{
??Id?int64?`json:"id"`
?}
?RemoveResponse?{
?}
?//?產(chǎn)品刪除
?//?產(chǎn)品詳情
?DetailRequest?{
??Id?int64?`json:"id"`
?}
?DetailResponse?{
??Id?????int64??`json:"id"`
??Name???string?`json:"name"`
??Desc???string?`json:"desc"`
??Stock??int64??`json:"stock"`
??Amount?int64??`json:"amount"`
??Status?int64??`json:"status"`
?}
?//?產(chǎn)品詳情
)
@server(
?jwt:?Auth
)
service?Product?{
?@handler?Create
?post?/api/product/create(CreateRequest)?returns?(CreateResponse)
?
?@handler?Update
?post?/api/product/update(UpdateRequest)?returns?(UpdateResponse)
?
?@handler?Remove
?post?/api/product/remove(RemoveRequest)?returns?(RemoveResponse)
?
?@handler?Detail
?post?/api/product/detail(DetailRequest)?returns?(DetailResponse)
}
運行模板生成命令
$?goctl?api?go?-api?./api/product.api?-dir?./api
4.3 生成 product rpc 服務
創(chuàng)建 proto 文件
$?vim?rpc/product.proto
編寫 proto 文件
syntax = "proto3";
package productclient;
option go_package = "product";
// 產(chǎn)品創(chuàng)建
message CreateRequest {
string Name = 1;
string Desc = 2;
int64 Stock = 3;
int64 Amount = 4;
int64 Status = 5;
}
message CreateResponse {
int64 id = 1;
}
// 產(chǎn)品創(chuàng)建
// 產(chǎn)品修改
message UpdateRequest {
int64 id = 1;
string Name = 2;
string Desc = 3;
int64 Stock = 4;
int64 Amount = 5;
int64 Status = 6;
}
message UpdateResponse {
}
// 產(chǎn)品修改
// 產(chǎn)品刪除
message RemoveRequest {
int64 id = 1;
}
message RemoveResponse {
}
// 產(chǎn)品刪除
// 產(chǎn)品詳情
message DetailRequest {
int64 id = 1;
}
message DetailResponse {
int64 id = 1;
string Name = 2;
string Desc = 3;
int64 Stock = 4;
int64 Amount = 5;
int64 Status = 6;
}
// 產(chǎn)品詳情
service Product {
rpc Create(CreateRequest) returns(CreateResponse);
rpc Update(UpdateRequest) returns(UpdateResponse);
rpc Remove(RemoveRequest) returns(RemoveResponse);
rpc Detail(DetailRequest) returns(DetailResponse);
}
運行模板生成命令
$?goctl?rpc?proto?-src?./rpc/product.proto?-dir?./rpc
4.4 編寫 product rpc 服務
4.4.1 修改配置文件
修改 product.yaml 配置文件
$?vim?rpc/etc/product.yaml
修改服務監(jiān)聽地址,端口號為0.0.0.0:9001, Etcd服務配置,Mysql服務配置,CacheRedis服務配置
Name:?product.rpc
ListenOn:?0.0.0.0:9001
Etcd:
??Hosts:
??-?etcd:2379
??Key:?product.rpc
Mysql:
??DataSource:?root:123456@tcp(mysql:3306)/mall?charset=utf8mb4&parseTime=true&loc=Asia%2FShanghai
CacheRedis:
-?Host:?redis:6379
??Type:?node?#?node可以不寫,可以設為cluster
??#?Pass:?xxx?#?如果有密碼
4.4.2 添加 product model 依賴
添加 Mysql服務配置,CacheRedis服務配置的實例化
$?vim?rpc/internal/config/config.go
package?config
import?(
?"github.com/tal-tech/go-zero/core/stores/cache"
?"github.com/tal-tech/go-zero/zrpc"
)
type?Config?struct?{
?zrpc.RpcServerConf
?Mysql?struct?{
??DataSource?string
?}
??
?CacheRedis?cache.CacheConf
}
注冊服務上下文 product model的依賴
$?vim?rpc/internal/svc/servicecontext.go
package?svc
import?(
?"mall/service/product/model"
?"mall/service/product/rpc/internal/config"
?"github.com/tal-tech/go-zero/core/stores/sqlx"
)
type?ServiceContext?struct?{
?Config?config.Config
????
?ProductModel?model.ProductModel
}
func?NewServiceContext(c?config.Config)?*ServiceContext?{
?conn?:=?sqlx.NewMysql(c.Mysql.DataSource)
?return?&ServiceContext{
??Config:???????c,
??ProductModel:?model.NewProductModel(conn,?c.CacheRedis),
?}
}
4.4.3 添加產(chǎn)品創(chuàng)建邏輯 Create
$?vim?rpc/internal/logic/createlogic.go
package?logic
import?(
?"context"
?"mall/service/product/model"
?"mall/service/product/rpc/internal/svc"
?"mall/service/product/rpc/product"
?"github.com/tal-tech/go-zero/core/logx"
?"google.golang.org/grpc/status"
)
type?CreateLogic?struct?{
?ctx????context.Context
?svcCtx?*svc.ServiceContext
?logx.Logger
}
func?NewCreateLogic(ctx?context.Context,?svcCtx?*svc.ServiceContext)?*CreateLogic?{
?return?&CreateLogic{
??ctx:????ctx,
??svcCtx:?svcCtx,
??Logger:?logx.WithContext(ctx),
?}
}
func?(l?*CreateLogic)?Create(in?*product.CreateRequest)?(*product.CreateResponse,?error)?{
?newProduct?:=?model.Product{
??Name:???in.Name,
??Desc:???in.Desc,
??Stock:??in.Stock,
??Amount:?in.Amount,
??Status:?in.Status,
?}
?res,?err?:=?l.svcCtx.ProductModel.Insert(&newProduct)
?if?err?!=?nil?{
??return?nil,?status.Error(500,?err.Error())
?}
?newProduct.Id,?err?=?res.LastInsertId()
?if?err?!=?nil?{
??return?nil,?status.Error(500,?err.Error())
?}
?return?&product.CreateResponse{
??Id:?newProduct.Id,
?},?nil
}
4.4.4 添加產(chǎn)品詳情邏輯 Detail
$?vim?rpc/internal/logic/detaillogic.go
package?logic
import?(
?"context"
?"mall/service/product/model"
?"mall/service/product/rpc/internal/svc"
?"mall/service/product/rpc/product"
?"github.com/tal-tech/go-zero/core/logx"
?"google.golang.org/grpc/status"
)
type?DetailLogic?struct?{
?ctx????context.Context
?svcCtx?*svc.ServiceContext
?logx.Logger
}
func?NewDetailLogic(ctx?context.Context,?svcCtx?*svc.ServiceContext)?*DetailLogic?{
?return?&DetailLogic{
??ctx:????ctx,
??svcCtx:?svcCtx,
??Logger:?logx.WithContext(ctx),
?}
}
func?(l?*DetailLogic)?Detail(in?*product.DetailRequest)?(*product.DetailResponse,?error)?{
?//?查詢產(chǎn)品是否存在
?res,?err?:=?l.svcCtx.ProductModel.FindOne(in.Id)
?if?err?!=?nil?{
??if?err?==?model.ErrNotFound?{
???return?nil,?status.Error(100,?"產(chǎn)品不存在")
??}
??return?nil,?status.Error(500,?err.Error())
?}
?return?&product.DetailResponse{
??Id:?????res.Id,
??Name:???res.Name,
??Desc:???res.Desc,
??Stock:??res.Stock,
??Amount:?res.Amount,
??Status:?res.Status,
?},?nil
}
4.4.5 添加產(chǎn)品更新邏輯 Update
$?vim?rpc/internal/logic/updatelogic.go
package?logic
import?(
?"context"
?"mall/service/product/model"
?"mall/service/product/rpc/internal/svc"
?"mall/service/product/rpc/product"
?"github.com/tal-tech/go-zero/core/logx"
?"google.golang.org/grpc/status"
)
type?UpdateLogic?struct?{
?ctx????context.Context
?svcCtx?*svc.ServiceContext
?logx.Logger
}
func?NewUpdateLogic(ctx?context.Context,?svcCtx?*svc.ServiceContext)?*UpdateLogic?{
?return?&UpdateLogic{
??ctx:????ctx,
??svcCtx:?svcCtx,
??Logger:?logx.WithContext(ctx),
?}
}
func?(l?*UpdateLogic)?Update(in?*product.UpdateRequest)?(*product.UpdateResponse,?error)?{
?//?查詢產(chǎn)品是否存在
?res,?err?:=?l.svcCtx.ProductModel.FindOne(in.Id)
?if?err?!=?nil?{
??if?err?==?model.ErrNotFound?{
???return?nil,?status.Error(100,?"產(chǎn)品不存在")
??}
??return?nil,?status.Error(500,?err.Error())
?}
?if?in.Name?!=?""?{
??res.Name?=?in.Name
?}
?if?in.Desc?!=?""?{
??res.Desc?=?in.Desc
?}
?if?in.Stock?!=?0?{
??res.Stock?=?in.Stock
?}
?if?in.Amount?!=?0?{
??res.Amount?=?in.Amount
?}
?if?in.Status?!=?0?{
??res.Status?=?in.Status
?}
?err?=?l.svcCtx.ProductModel.Update(res)
?if?err?!=?nil?{
??return?nil,?status.Error(500,?err.Error())
?}
?return?&product.UpdateResponse{},?nil
}
4.4.6 添加產(chǎn)品刪除邏輯 Remove
$?vim?rpc/internal/logic/removelogic.go
package?logic
import?(
?"context"
?"mall/service/product/model"
?"mall/service/product/rpc/internal/svc"
?"mall/service/product/rpc/product"
?"github.com/tal-tech/go-zero/core/logx"
?"google.golang.org/grpc/status"
)
type?RemoveLogic?struct?{
?ctx????context.Context
?svcCtx?*svc.ServiceContext
?logx.Logger
}
func?NewRemoveLogic(ctx?context.Context,?svcCtx?*svc.ServiceContext)?*RemoveLogic?{
?return?&RemoveLogic{
??ctx:????ctx,
??svcCtx:?svcCtx,
??Logger:?logx.WithContext(ctx),
?}
}
func?(l?*RemoveLogic)?Remove(in?*product.RemoveRequest)?(*product.RemoveResponse,?error)?{
?//?查詢產(chǎn)品是否存在
?res,?err?:=?l.svcCtx.ProductModel.FindOne(in.Id)
?if?err?!=?nil?{
??if?err?==?model.ErrNotFound?{
???return?nil,?status.Error(100,?"產(chǎn)品不存在")
??}
??return?nil,?status.Error(500,?err.Error())
?}
?err?=?l.svcCtx.ProductModel.Delete(res.Id)
?if?err?!=?nil?{
??return?nil,?status.Error(500,?err.Error())
?}
?return?&product.RemoveResponse{},?nil
}
4.5 編寫 product api 服務
4.5.1 修改配置文件
修改 product.yaml 配置文件
$?vim?api/etc/product.yaml
修改服務地址,端口號為0.0.0.0:8001, Mysql服務配置,CacheRedis服務配置,Auth驗證配置
Name:?Product
Host:?0.0.0.0
Port:?8001
Mysql:
??DataSource:?root:123456@tcp(mysql:3306)/mall?charset=utf8mb4&parseTime=true&loc=Asia%2FShanghai
CacheRedis:
-?Host:?redis:6379
??Type:?node?#?node可以不寫,可以設為cluster
??#?Pass:?xxx?#?如果有密碼
Auth:
??AccessSecret:?uOvKLmVfztaXGpNYd4Z0I1SiT7MweJhl
??AccessExpire:?86400
4.5.2 添加 product rpc 依賴
添加 product rpc服務配置
$?vim?api/etc/product.yaml
Name:?Product
Host:?0.0.0.0
Port:?8001
...
ProductRpc:
??Etcd:
????Hosts:
????-?etcd:2379
????Key:?product.rpc
添加 product rpc服務配置的實例化
$?vim?api/internal/config/config.go
package?config
import?(
?"github.com/tal-tech/go-zero/rest"
?"github.com/tal-tech/go-zero/zrpc"
)
type?Config?struct?{
?rest.RestConf
?Auth?struct?{
??AccessSecret?string
??AccessExpire?int64
?}
?ProductRpc?zrpc.RpcClientConf
}
注冊服務上下文 product rpc的依賴
$?vim?api/internal/svc/servicecontext.go
package?svc
import?(
?"mall/service/product/api/internal/config"
?"mall/service/product/rpc/productclient"
?"github.com/tal-tech/go-zero/zrpc"
)
type?ServiceContext?struct?{
?Config?config.Config
????
?ProductRpc?productclient.Product
}
func?NewServiceContext(c?config.Config)?*ServiceContext?{
?return?&ServiceContext{
??Config:?????c,
??ProductRpc:?productclient.NewProduct(zrpc.MustNewClient(c.ProductRpc)),
?}
}
4.5.3 添加產(chǎn)品創(chuàng)建邏輯 Create
$?vim?api/internal/logic/createlogic.go
package?logic
import?(
?"context"
?"mall/service/product/api/internal/svc"
?"mall/service/product/api/internal/types"
?"mall/service/product/rpc/product"
?"github.com/tal-tech/go-zero/core/logx"
)
type?CreateLogic?struct?{
?logx.Logger
?ctx????context.Context
?svcCtx?*svc.ServiceContext
}
func?NewCreateLogic(ctx?context.Context,?svcCtx?*svc.ServiceContext)?CreateLogic?{
?return?CreateLogic{
??Logger:?logx.WithContext(ctx),
??ctx:????ctx,
??svcCtx:?svcCtx,
?}
}
func?(l?*CreateLogic)?Create(req?types.CreateRequest)?(resp?*types.CreateResponse,?err?error)?{
?res,?err?:=?l.svcCtx.ProductRpc.Create(l.ctx,?&product.CreateRequest{
??Name:???req.Name,
??Desc:???req.Desc,
??Stock:??req.Stock,
??Amount:?req.Amount,
??Status:?req.Status,
?})
?if?err?!=?nil?{
??return?nil,?err
?}
?return?&types.CreateResponse{
??Id:?res.Id,
?},?nil
}
4.5.4 添加產(chǎn)品詳情邏輯 Detail
$?vim?api/internal/logic/detaillogic.go
package?logic
import?(
?"context"
?"mall/service/product/api/internal/svc"
?"mall/service/product/api/internal/types"
?"mall/service/product/rpc/product"
?"github.com/tal-tech/go-zero/core/logx"
)
type?DetailLogic?struct?{
?logx.Logger
?ctx????context.Context
?svcCtx?*svc.ServiceContext
}
func?NewDetailLogic(ctx?context.Context,?svcCtx?*svc.ServiceContext)?DetailLogic?{
?return?DetailLogic{
??Logger:?logx.WithContext(ctx),
??ctx:????ctx,
??svcCtx:?svcCtx,
?}
}
func?(l?*DetailLogic)?Detail(req?types.DetailRequest)?(resp?*types.DetailResponse,?err?error)?{
?res,?err?:=?l.svcCtx.ProductRpc.Detail(l.ctx,?&product.DetailRequest{
??Id:?req.Id,
?})
?if?err?!=?nil?{
??return?nil,?err
?}
?return?&types.DetailResponse{
??Id:?????res.Id,
??Name:???res.Name,
??Desc:???res.Desc,
??Stock:??res.Stock,
??Amount:?res.Amount,
??Status:?res.Status,
?},?nil
}
4.5.5 添加產(chǎn)品更新邏輯 Update
$?vim?api/internal/logic/updatelogic.go
package?logic
import?(
?"context"
?"mall/service/product/api/internal/svc"
?"mall/service/product/api/internal/types"
?"mall/service/product/rpc/product"
?"github.com/tal-tech/go-zero/core/logx"
)
type?UpdateLogic?struct?{
?logx.Logger
?ctx????context.Context
?svcCtx?*svc.ServiceContext
}
func?NewUpdateLogic(ctx?context.Context,?svcCtx?*svc.ServiceContext)?UpdateLogic?{
?return?UpdateLogic{
??Logger:?logx.WithContext(ctx),
??ctx:????ctx,
??svcCtx:?svcCtx,
?}
}
func?(l?*UpdateLogic)?Update(req?types.UpdateRequest)?(resp?*types.UpdateResponse,?err?error)?{
?_,?err?=?l.svcCtx.ProductRpc.Update(l.ctx,?&product.UpdateRequest{
??Id:?????req.Id,
??Name:???req.Name,
??Desc:???req.Desc,
??Stock:??req.Stock,
??Amount:?req.Amount,
??Status:?req.Status,
?})
?if?err?!=?nil?{
??return?nil,?err
?}
?return?&types.UpdateResponse{},?nil
}
4.5.6 添加產(chǎn)品刪除邏輯 Remove
$?vim?api/internal/logic/removelogic.go
package?logic
import?(
?"context"
?"mall/service/product/api/internal/svc"
?"mall/service/product/api/internal/types"
?"mall/service/product/rpc/product"
?"github.com/tal-tech/go-zero/core/logx"
)
type?RemoveLogic?struct?{
?logx.Logger
?ctx????context.Context
?svcCtx?*svc.ServiceContext
}
func?NewRemoveLogic(ctx?context.Context,?svcCtx?*svc.ServiceContext)?RemoveLogic?{
?return?RemoveLogic{
??Logger:?logx.WithContext(ctx),
??ctx:????ctx,
??svcCtx:?svcCtx,
?}
}
func?(l?*RemoveLogic)?Remove(req?types.RemoveRequest)?(resp?*types.RemoveResponse,?err?error)?{
?_,?err?=?l.svcCtx.ProductRpc.Remove(l.ctx,?&product.RemoveRequest{
??Id:?req.Id,
?})
?if?err?!=?nil?{
??return?nil,?err
?}
?return?&types.RemoveResponse{},?nil
}
4.6 啟動 product rpc 服務
提示:啟動服務需要在
golang容器中啟動
$?cd?mall/service/product/rpc
$?go?run?product.go?-f?etc/product.yaml
Starting?rpc?server?at?127.0.0.1:9001...
4.7 啟動 product api 服務
提示:啟動服務需要在
golang容器中啟動
$?cd?mall/service/product/api
$?go?run?product.go?-f?etc/product.yaml
Starting?server?at?0.0.0.0:8001...
項目地址
https://github.com/zeromicro/go-zero
歡迎使用 go-zero 并 star 支持我們!
推薦閱讀
評論
圖片
表情
