前言
書接上回,繼續更新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文件有自定義的修改之後, 就不建議使用這個工具了, 使用方法如下
-
安裝最新的sql2pb
go install github.com/Mikaelemmmm/sql2pb@latest -
命令示例:
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)
-
使用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文件
- 將json文件導入到Apifox中
打開Apifox, 新建接口項目, 點擊這裏導入json文件
將json文件拖進去即可
完成之後-->點擊這裏-->進入選擇開發環境-->進行一個端口的配置
這裏的地址就是我對應的api服務的地址(yaml文件中配置)
- 啓動程序後, 進行測試
還記得剛剛我改名的main.go文件嗎,一個在api層,一個在rpc層,現在分別運行它們
將它們同時在控制枱Terminal打開,分別運行命令go run main.go
可以看到它們監聽了兩個端口,一個1001一個2001
接下來就可以在Apifox中進行測試了
從左至右分別點擊這三個地方(數據可以自動生成也可以自己寫)
然後發現返回null和200(因為我沒有定義規範的返回給前端的字段,所以返回null代表正常)
- 查詢數據庫,確保數據生成
最後查看數據庫中是否出現了這條記錄
可以看到記錄新增成功, 圓滿完成!
總結
這篇文章分享瞭如何使用gozero開發文章服務的增加功能,強烈建議你跟着我的步驟操練一遍,也可以嘗試用相同的思路實現刪除、查詢功能。
我將繼續更新Go-Zero系列文章,如果你對Go語言或者微服務感興趣,歡迎關注我