1、学习课程

带你十天轻松搞定 Go 微服务系列(四)

2、产品服务(product)

进入服务工作区

$ cd mall/service/product

2.1 生成 product model 模型

创立 sql 文件

$ vim model/product.sql

编写 sql 文件

CREATE TABLE `product` ( `id` bigint unsigned NOT NULL AUTO_INCREMENT, `name` varchar(255)  NOT NULL DEFAULT '' COMMENT '产品名称', `desc` varchar(255)  NOT NULL DEFAULT '' COMMENT '产品描述',        `stock` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '产品库存', `amount` int(10) unsigned NOT NULL DEFAULT '0'  COMMENT '产品金额', `status` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '产品状态', `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;

运行模板生成命令(和昨天一样要在golang容器下运行)

$ goctl model mysql ddl -src ./model/product.sql -dir ./model -c

2.2 生成 product api 服务

创立 api 文件

$ vim api/product.api

编写 api 文件

type ( // 产品创立 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"` } // 产品创立 // 产品批改 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 { } // 产品批改 // 产品删除 RemoveRequest {  Id int64 `json:"id"` } RemoveResponse { } // 产品删除 // 产品详情 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"` } // 产品详情)@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)}

运行模板生成命令(一样要在golang容器下运行)

$ goctl api go -api ./api/product.api -dir ./api

2.3 生成 product rpc 服务

创立 proto 文件

$ vim rpc/product.proto

编写 proto 文件

syntax = "proto3";package productclient;option go_package = "product";// 产品创立message CreateRequest {    string Name = 1;    string Desc = 2;    int64 Stock = 3;    int64 Amount = 4;    int64 Status = 5;}message CreateResponse {    int64 id = 1;}// 产品创立// 产品批改message UpdateRequest {    int64 id = 1;    string Name = 2;    string Desc = 3;    int64 Stock = 4;    int64 Amount = 5;    int64 Status = 6;}message UpdateResponse {}// 产品批改// 产品删除message RemoveRequest {    int64 id = 1;}message RemoveResponse {}// 产品删除// 产品详情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;}// 产品详情service Product {    rpc Create(CreateRequest) returns(CreateResponse);    rpc Update(UpdateRequest) returns(UpdateResponse);    rpc Remove(RemoveRequest) returns(RemoveResponse);    rpc Detail(DetailRequest) returns(DetailResponse);}

运行模板生成命令(一样要在golang容器下运行)

$ goctl rpc proto -src ./rpc/product.proto -dir ./rpc

2.4 编写 product rpc 服务

2.4.1 批改配置文件

批改 product.yaml 配置文件

$ vim rpc/etc/product.yaml

批改服务监听地址,端口号为0.0.0.0:9001,Etcd 服务配置,Mysql 服务配置,CacheRedis 服务配置

Name: product.rpcListenOn: 0.0.0.0:9001Etcd:  Hosts:  - etcd:2379  Key: product.rpcMysql:  DataSource: root:123456@tcp(mysql:3306)/mall?charset=utf8mb4&parseTime=true&loc=Asia%2FShanghaiCacheRedis:- Host: redis:6379  Type: node # node能够不写,能够设为cluster  # Pass: xxx # 如果有明码

2.4.2 增加 product model 依赖

增加 Mysql 服务配置,CacheRedis 服务配置的实例化

$ vim rpc/internal/config/config.go
package configimport ( "github.com/zeromicro/go-zero/core/stores/cache" "github.com/zeromicro/go-zero/zrpc")type Config struct { zrpc.RpcServerConf Mysql struct {  DataSource string }   CacheRedis cache.CacheConf}

注册服务上下文 product model 的依赖

$ vim rpc/internal/svc/servicecontext.go
package svcimport ( "mall/service/product/model" "mall/service/product/rpc/internal/config" "github.com/zeromicro/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), }}

2.4.3 增加产品创立逻辑 Create

$ vim rpc/internal/logic/createlogic.go
package logicimport ( "context" "mall/service/product/model" "mall/service/product/rpc/internal/svc" "mall/service/product/rpc/product" "github.com/zeromicro/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}

2.4.4 增加产品详情逻辑 Detail

$ vim rpc/internal/logic/detaillogic.go
package logicimport ( "context" "mall/service/product/model" "mall/service/product/rpc/internal/svc" "mall/service/product/rpc/product" "github.com/zeromicro/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) { // 查问产品是否存在 res, err := l.svcCtx.ProductModel.FindOne(in.Id) if err != nil {  if err == model.ErrNotFound {   return nil, status.Error(100, "产品不存在")  }  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}

2.4.5 增加产品更新逻辑 Update

$ vim rpc/internal/logic/updatelogic.go
package logicimport ( "context" "mall/service/product/model" "mall/service/product/rpc/internal/svc" "mall/service/product/rpc/product" "github.com/zeromicro/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) { // 查问产品是否存在 res, err := l.svcCtx.ProductModel.FindOne(in.Id) if err != nil {  if err == model.ErrNotFound {   return nil, status.Error(100, "产品不存在")  }  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}

2.4.6 增加产品删除逻辑 Remove

$ vim rpc/internal/logic/removelogic.go
package logicimport ( "context" "mall/service/product/model" "mall/service/product/rpc/internal/svc" "mall/service/product/rpc/product" "github.com/zeromicro/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) { // 查问产品是否存在 res, err := l.svcCtx.ProductModel.FindOne(in.Id) if err != nil {  if err == model.ErrNotFound {   return nil, status.Error(100, "产品不存在")  }  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}

2.5 编写 product api 服务

2.5.1 批改配置文件

批改 product.yaml 配置文件

$ vim api/etc/product.yaml

批改服务地址,端口号为0.0.0.0:8001,Mysql 服务配置,CacheRedis 服务配置,Auth 验证配置

Name: ProductHost: 0.0.0.0Port: 8001Mysql:  DataSource: root:123456@tcp(mysql:3306)/mall?charset=utf8mb4&parseTime=true&loc=Asia%2FShanghaiCacheRedis:- Host: redis:6379  Type: node # node能够不写,能够设为cluster  # Pass: xxx # 如果有明码Auth:  AccessSecret: uOvKLmVfztaXGpNYd4Z0I1SiT7MweJhl  AccessExpire: 86400

2.5.2 增加 product rpc 依赖

增加 product rpc 服务配置

$ vim api/etc/product.yaml
Name: ProductHost: 0.0.0.0Port: 8001...ProductRpc:  Etcd:    Hosts:    - etcd:2379    Key: product.rpc

增加 product rpc 服务配置的实例化

$ vim api/internal/config/config.go
package configimport ( "github.com/zeromicro/go-zero/rest" "github.com/zeromicro/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 svcimport ( "mall/service/product/api/internal/config" "mall/service/product/rpc/productclient" "github.com/zeromicro/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)), }}

2.5.3 增加产品创立逻辑 Create

$ vim api/internal/logic/createlogic.go
package logicimport ( "context" "mall/service/product/api/internal/svc" "mall/service/product/api/internal/types" "mall/service/product/rpc/product" "github.com/zeromicro/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}

2.5.4 增加产品详情逻辑 Detail

$ vim api/internal/logic/detaillogic.go
package logicimport ( "context" "mall/service/product/api/internal/svc" "mall/service/product/api/internal/types" "mall/service/product/rpc/product" "github.com/zeromicro/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}

2.5.5 增加产品更新逻辑 Update

$ vim api/internal/logic/updatelogic.go
package logicimport ( "context" "mall/service/product/api/internal/svc" "mall/service/product/api/internal/types" "mall/service/product/rpc/product" "github.com/zeromicro/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}

2.5.6 增加产品删除逻辑 Remove

$ vim api/internal/logic/removelogic.go
package logicimport ( "context" "mall/service/product/api/internal/svc" "mall/service/product/api/internal/types" "mall/service/product/rpc/product" "github.com/zeromicro/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}

2.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...

2.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...