本文为稀土掘金技术社区首发签约文章,30天内禁止转载,30天后未获授权禁止转载,侵权必究!
前言
上一篇文章带你了解了GoZero的开发技巧 & 整体开发流程,本文将继续使用 Go-zero 提供的工具和组件,从零开始逐步构建一个基本的微服务项目。手把手带你完成:项目初始化+需求分析+表结构设计+api+rpc+goctl+apifox调试+细节处理。带你实现一个完整微服务的开发。
实战前准备
首先需要你在本地安装goctl、protoc、go-zero
,下载教学和地址点击这里,按照教程操作即可,非常简单。
下面按顺序和我操作吧,对整体开发流程不清楚的同学务必先看我上一篇文章: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换成自己的对应的数据)
shell
goctl model mysql datasource -url="${username}:${passwd}@tcp(${host}:${port})/${dbname}" -table="${tables}" -dir="./" -cache=true --style=goZero
一键生成:
3 | 设计api层
在api
目录下新建文件:
在article.api
中定义文章服务的请求和响应
go
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
go
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
,输入代码
shell
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
shell
go install github.com/Mikaelemmmm/sql2pb@latest
- 命令示例:
shell
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
文件, 你也可以根据自己的需求往里面增加内容
go
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
,输入代码:
shell
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
文件, 写入以下代码:
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
文件
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
文件
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
文件, 写入以下代码:
go
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
文件
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
文件
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
文件:
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
文件:
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, 确认安装是否成功:
shell
go get -u github.com/zeromicro/goctl-swagger
goctl-swagger -v
在main.api
下打开Terminal
, 输入以下代码:
shell
goctl api plugin -plugin goctl-swagger="swagger -filename main.json" -api main.api -dir .
不出意外就会生成一个main.json
文件
2. 将json文件导入到Apifox中
打开Apifox, 新建接口项目, 点击这里导入json
文件
将json
文件拖进去即可
完成之后-->点击这里-->进入选择开发环境-->进行一个端口的配置
这里的地址就是我对应的api
服务的地址(yaml
文件中配置)
3. 启动程序后, 进行测试
还记得刚刚我改名的main.go
文件吗,一个在api
层,一个在rpc
层,现在分别运行它们
将它们同时在控制台Terminal
打开,分别运行命令go run main.go
可以看到它们监听了两个端口,一个1001
一个2001
接下来就可以在Apifox中进行测试了
从左至右分别点击这三个地方(数据可以自动生成也可以自己写)
然后发现返回null
和200
(因为我没有定义规范的返回给前端的字段,所以返回null
代表正常)
4. 查询数据库,确保数据生成
最后查看数据库中是否出现了这条记录
可以看到记录新增成功, 圆满完成!
总结
这篇文章分享了如何使用gozero开发文章服务的增加功能,强烈建议你跟着我的步骤操练一遍,也可以尝试用相同的思路实现删除、查询功能。
我将继续更新Go-Zero系列文章,如果你对Go语言或者微服务感兴趣,欢迎关注我的掘金账号,也欢迎在掘金私信我。