博客 / 詳情

返回

Go-Zero從0到1實現微服務項目開發(二)

前言

書接上回,繼續更新GoZero微服務實戰系列文章。

上一篇被GoZero作者萬總點讚了,更文動力倍增,也建議大家先看巧一篇,歡迎粉絲股東們三連支持一波:Go-zero微服務快速入門和最佳實踐(一)

本文將繼續使用 Go-zero 提供的工具和組件,從零開始逐步構建一個基本的微服務項目。手把手帶你完成:項目初始化+需求分析+表結構設計+api+rpc+goctl+apifox調試+細節處理。帶你實現一個完整微服務的開發。

實戰前準備

首先需要你在本地安裝goctl、protoc、go-zero,goctl安裝,按照教程操作即可,非常簡單。

下面按順序和我操作吧,對整體開發流程不清楚的同學務必先看我上一篇文章:GoZero的開發技巧 & 整體開發流程

實戰開始

1 | 新建項目(本文使用GoLand)

左上角File-->選擇New-->點擊Project(如果是第一次使用直接點擊New Project即可)

選擇新建項目的文件夾以及命名,選擇Go的版本(我使用的是Go 1.22.1)

新建文件目錄如下

2 | 設計庫和表,生成model(本文以文章article表舉例,帶你實現增刪改查基礎功能)

數據庫表結構設計

快速定位到model目錄下執行該命令(右鍵model-->找到Open In-->點擊Terminal)

使用goctl命令生成model(username、passwd、host、port、dbname、tables換成自己的對應的數據)

goctl model mysql datasource -url="${username}:${passwd}@tcp(${host}:${port})/${dbname}" -table="${tables}" -dir="./" -cache=true --style=goZero

一鍵生成:

3 | 設計api層

在api目錄下新建文件:

在article.api中定義文章服務的請求和響應

syntax = "v1"

info (
    title:   "文章服務"
    desc:    "文章服務"
    version: "v1"
)

// 數據庫中對應的article表
type Article {
    Id       int64  `json:"id"`
    Title    string `json:"title"`
    Content  string `json:"content"`
}
//---------------------------Req&Resp------------------------------
// 獲取文章列表
type (
    GetArticleListReq {
    }

    GetArticleListResp {
        Articles []Article `json:"Articles"`
    }
)
// 創建文章
type (
    CreateArticleReq {
        Title   string `json:"title"`
        Content string `json:"content"`
    }
    CreateArticleResp {
    }
)
// 刪除文章
type (
    DeleteArticleReq {
        Id int64 `json:"id"`
    }
    DeleteArticleResp {
    }
)
// 修改文章
type (
    UpdateArticleReq {
        Id      int64  `json:"id"`
        Title   string `json:"title"`
        Content string `json:"content"`
    }
    UpdateArticleResp {
    }
)

在main.api中定義文章服務的API

syntax = "v1"

info (
    title:   "文章服務"
    desc:    "文章服務"
    version: "v1"
)

// 導入article.api,直接引用
import (
    "article/article.api"
)

// 服務的相關配置,這裏分別是前綴和分組
@server (
    prefix: article/v1
    group:  article
)
service article {
    @doc "獲得文章列表"
    @handler getArticles
    post /getArticles (GetArticleListReq) returns (GetArticleListResp)

    @doc "創建文章"
    @handler createArticle
    post /createArticle (CreateArticleReq) returns (CreateArticleResp)

    @doc "刪除文章"
    @handler deleteArticle
    post /deleteArticle (DeleteArticleReq) returns (DeleteArticleResp)

    @doc "修改文章"
    @handler updateArticle
    post /updateArticle (UpdateArticleReq) returns (UpdateArticleResp)
}

至此api層已經定義好了,接下來使用goctl代碼自動生成

和剛剛一樣,右鍵main.api然後打開Terminal,輸入代碼

goctl api go -api main.api -dir ../ --style=goZero 

會自動生成etc、internal文件夾以及article.go文件(我習慣把article.go文件改成main.go文件:如果我們後續有多個微服務,執行goctl命令的時候就不用頻繁切換文件名稱了)

在這裏只需要在意幾個配置文件(article.yaml, config.go, serviceContext.go)以及需要編寫代碼的logic目錄即可, 暫時先不管, 將rpc層也生成好後一起配置。

4 | 編寫rpc層

在rpc下新建pb文件夾, 在pb文件夾裏新建article.proto文件

在編寫proto文件的時候, 如果是第一次編寫的話, 可以使用sql2pb工具自動生成, 一旦我們的proto文件有自定義的修改之後, 就不建議使用這個工具了, 使用方法如下

  1. 安裝最新的sql2pb

    go install github.com/Mikaelemmmm/sql2pb@latest
  2. 命令示例:

    sql2pb -go_package ./pb -host localhost -package pb -password lps123456 -port 3306 -schema zero-demo -service_name article -user root > article.proto

    這個命令可以直接將我的zero-demo數據庫下所有的表內容都生成到article.proto文件中
    這是自動生成的article.proto文件, 你也可以根據自己的需求往裏面增加內容

    syntax = "proto3";
    
    option go_package ="./pb";
    
    package pb;
    
    // ------------------------------------ 
    // Messages
    // ------------------------------------ 
    
    //--------------------------------article--------------------------------
    message Article {
      int64 id = 1; //id
      string title = 2; //title
      string content = 3; //content
    }
    
    message AddArticleReq {
      string title = 1; //title
      string content = 2; //content
    }
    
    message AddArticleResp {
    }
    
    message UpdateArticleReq {
      int64 id = 1; //id
      string title = 2; //title
      string content = 3; //content
    }
    
    message UpdateArticleResp {
    }
    
    message DelArticleReq {
      int64 id = 1; //id
    }
    
    message DelArticleResp {
    }
    
    message GetArticleByIdReq {
      int64 id = 1; //id
    }
    
    message GetArticleByIdResp {
      Article article = 1; //article
    }
    
    message SearchArticleReq {
      int64 page = 1; //page
      int64 limit = 2; //limit
      int64 id = 3; //id
      string title = 4; //title
      string content = 5; //content
    }
    
    message SearchArticleResp {
      repeated Article article = 1; //article
    }
    
    
    
    // ------------------------------------ 
    // Rpc Func
    // ------------------------------------ 
    
    service article{ 
    
      //-----------------------article----------------------- 
      rpc AddArticle(AddArticleReq) returns (AddArticleResp); 
      rpc UpdateArticle(UpdateArticleReq) returns (UpdateArticleResp); 
      rpc DelArticle(DelArticleReq) returns (DelArticleResp); 
      rpc GetArticleById(GetArticleByIdReq) returns (GetArticleByIdResp); 
      rpc SearchArticle(SearchArticleReq) returns (SearchArticleResp); 
    
    }

    下一步就是通過proto文件自動生成rpc層其他代碼

和之前説的一樣,右鍵article.proto然後打開Terminal,輸入代碼:

goctl rpc protoc article.proto --go_out=../ --go-grpc_out=../ --zrpc_out=../ --style=goZero

注意: 如果你是Windows電腦,運行後可能會出現一個invalid UTF-8 encoding的問題, 將左下角的文件格式改為UTF-8即可。(更建議你按照 git bash,從根本上解決這個問題。)

運行成功後又會生成好幾個文件

這裏我同樣將article.go改成了main.go

5 | 配置api層和rpc層

OK,現在沒有能自動生成的代碼了, 需要自己手動敲代碼實現業務邏輯了。

配置api層

打開api/etc/article.yaml文件, 寫入以下代碼:

Name: article-api #服務名稱
Host: 127.0.0.1 #監聽地址
Port: 1001 #監聽端口
Mode: dev #運行模式
# 配置MySQL Redis
DB:
  DataSource: root:lps123456@tcp(127.0.0.1:3306)/zero-demo?charset=utf8mb4&parseTime=true&loc=Asia%2FShanghai
Cache:
  - Host: 127.0.0.1:6379
    Pass:
# 配置rpc客户端, 後面需要調用rpc中的方法
ArticleRpcConf:
  Endpoints:
    - 127.0.0.1:2001
  NonBlock: true

接下來是api/internal/config/config.go文件

package config

import (
   "github.com/zeromicro/go-zero/core/stores/cache"
   "github.com/zeromicro/go-zero/rest"
   "github.com/zeromicro/go-zero/zrpc"
)
type Config struct {
   rest.RestConf
   DB struct {
      DataSource string
   }
   Cache cache.CacheConf
   ArticleRpcConf zrpc.RpcClientConf
}

最後是api/internal/svc/serviceContext.go文件

package svc

import (
   "GoZeroDemo/app/article/cmd/api/internal/config"
   "GoZeroDemo/app/article/cmd/rpc/article"
   "github.com/zeromicro/go-zero/zrpc"
)

type ServiceContext struct {
   Config  config.Config
   ArticleRpc article.ArticleZrpcClient
}

func NewServiceContext(c config.Config) *ServiceContext {
   return &ServiceContext{
      Config:  c,
      ArticleRpc: article.NewArticleZrpcClient(zrpc.MustNewClient(c.ArticleRpcConf)),
   }
}

api層的服務就配置好了

配置rpc層

打開rpc/etc/article.yaml文件, 寫入以下代碼:

Name: article-rpc #服務名稱
ListenOn: 127.0.0.1:2001 #監聽地址
Mode: dev #運行模式
# 配置Redis
Redis:
  Host: 127.0.0.1:6379
  Type: node
  Pass:
  Key: article-rpc
# 配置MySQL
DB:
  DataSource: root:lps123456@tcp(127.0.0.1:3306)/zero-demo?charset=utf8mb4&parseTime=true&loc=Asia%2FShanghai
Cache:
  - Host: 127.0.0.1:6379
    Pass:

接下來是rpc/internal/config/config.go文件

package config

import (
   "github.com/zeromicro/go-zero/core/stores/cache"
   "github.com/zeromicro/go-zero/zrpc"
)

type Config struct {
   zrpc.RpcServerConf
   DB struct {
      DataSource string
   }
   Cache cache.CacheConf
}

最後是rpc/internal/svc/serviceContext.go文件

package svc

import (
   "GoZeroDemo/app/article/cmd/rpc/internal/config"
   "GoZeroDemo/app/article/model"
   "github.com/zeromicro/go-zero/core/stores/redis"
   "github.com/zeromicro/go-zero/core/stores/sqlx"
)

type ServiceContext struct {
   Config       config.Config
   RedisClient  *redis.Redis
   ArticleModel model.ArticleModel
}

func NewServiceContext(c config.Config) *ServiceContext {
   return &ServiceContext{
      Config:       c,
      ArticleModel: model.NewArticleModel(sqlx.NewMysql(c.DB.DataSource), c.Cache),
   }
}

至此都配置好了, 接下來就是編寫業務邏輯代碼

6 | 編寫api層和rpc層下logic代碼

這裏我就拿增加文章做示例

首先編寫api下的logic中的createArticleLogic.go文件:

package article

import (
   "GoZeroDemo/app/article/cmd/rpc/article"
   "context"

   "GoZeroDemo/app/article/cmd/api/internal/svc"
   "GoZeroDemo/app/article/cmd/api/internal/types"

   "github.com/zeromicro/go-zero/core/logx"
)

type CreateArticleLogic struct {
   logx.Logger
   ctx    context.Context
   svcCtx *svc.ServiceContext
}

func NewCreateArticleLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CreateArticleLogic {
   return &CreateArticleLogic{
      Logger: logx.WithContext(ctx),
      ctx:    ctx,
      svcCtx: svcCtx,
   }
}

func (l *CreateArticleLogic) CreateArticle(req *types.CreateArticleReq) (resp *types.CreateArticleResp, err error) {
   // 這裏就是調用rpc下的AddArticle方法 
   _, err = l.svcCtx.ArticleRpc.AddArticle(l.ctx, &article.AddArticleReq{
      Title:   req.Title,
      Content: req.Content,
   })
   if err != nil {
      return nil, err
   }
   return

   return
}

接下來完成rpc中的AddArticle方法, 編寫addArticleLogic.go文件:

package logic

import (
   "GoZeroDemo/app/article/cmd/rpc/internal/svc"
   "GoZeroDemo/app/article/cmd/rpc/pb"
   "GoZeroDemo/app/article/model"
   "context"

   "github.com/zeromicro/go-zero/core/logx"
)

type AddArticleLogic struct {
   ctx    context.Context
   svcCtx *svc.ServiceContext
   logx.Logger
}

func NewAddArticleLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AddArticleLogic {
   return &AddArticleLogic{
      ctx:    ctx,
      svcCtx: svcCtx,
      Logger: logx.WithContext(ctx),
   }
}

// -----------------------article-----------------------
func (l *AddArticleLogic) AddArticle(in *pb.AddArticleReq) (*pb.AddArticleResp, error) {
   article := new(model.Article)
   article.Title = in.Title
   article.Content = in.Content
   // 調用model層的方法
   _, err := l.svcCtx.ArticleModel.Insert(l.ctx, article)
   if err != nil {
      return nil, err
   }
   return &pb.AddArticleResp{}, nil
}

接下來就可以進行測試了

7 | 自動生成接口文檔並測試(使用Apifox)

  1. 使用swagg生成json文件
    下載goctl-swagger, 確認安裝是否成功:

    go get -u github.com/zeromicro/goctl-swagger
    goctl-swagger -v

    在main.api下打開Terminal, 輸入以下代碼:

    goctl api plugin -plugin goctl-swagger="swagger -filename main.json" -api main.api -dir .

    不出意外就會生成一個main.json文件

  1. 將json文件導入到Apifox中
    打開Apifox, 新建接口項目, 點擊這裏導入json文件

將json文件拖進去即可

完成之後-->點擊這裏-->進入選擇開發環境-->進行一個端口的配置

這裏的地址就是我對應的api服務的地址(yaml文件中配置)

  1. 啓動程序後, 進行測試
    還記得剛剛我改名的main.go文件嗎,一個在api層,一個在rpc層,現在分別運行它們

將它們同時在控制枱Terminal打開,分別運行命令go run main.go

可以看到它們監聽了兩個端口,一個1001一個2001
接下來就可以在Apifox中進行測試了
從左至右分別點擊這三個地方(數據可以自動生成也可以自己寫)

然後發現返回null和200(因為我沒有定義規範的返回給前端的字段,所以返回null代表正常)

  1. 查詢數據庫,確保數據生成
    最後查看數據庫中是否出現了這條記錄

可以看到記錄新增成功, 圓滿完成!

總結

這篇文章分享瞭如何使用gozero開發文章服務的增加功能,強烈建議你跟着我的步驟操練一遍,也可以嘗試用相同的思路實現刪除、查詢功能。

我將繼續更新Go-Zero系列文章,如果你對Go語言或者微服務感興趣,歡迎關注我

user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.