GoZero
goctl
goctl-api
- goctl为我们避免了手工创建各级目录等,也会为我们生成简单http的代码,我们只需要编写api文件即可。
这是一个hello.api文件编写好api文件后,执行以下命令就会自动帮我们生成代码以及目录1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17//api语法版本
syntax = "v1"
//请求路径
type Request {
Name string `path:"name,options=you|me"`
}
//返回值类型
type Response {
Message string `json:"message"`
}
// 拦截器以及请求方式
service hello-api {
@handler HelloHandler
get /from/:name (Request) returns (Response)
}结果:1
2// 这个表示为所有api文件生成代码,指定文件可将*号换成指定api文件, dir .. 创建的文件在上一级目录下
goctl api go -api *.api dir ../ --style=gozero
- 基础代码生成后,别忘记下载依赖
1
2//该命令可以一键下载依赖
go mod tidy
goctl生成代码执行流程
先看目录结构
这里的入口文件即是hello.go文件,所以我们先看入口文件
- hello.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34package main
import (
"flag"
"fmt"
"hello/internal/config"
"hello/internal/handler"
"hello/internal/svc"
"github.com/zeromicro/go-zero/core/conf"
"github.com/zeromicro/go-zero/rest"
)
//这里是将配置文件导入
var configFile = flag.String("f", "etc/hello-api.yaml", "the config file")
func main() {
//先解析配置文件
flag.Parse()
//将配置文件给到自定义的配置文件类型的变量c
var c config.Config
conf.MustLoad(*configFile, &c)
//获取http服务
server := rest.MustNewServer(c.RestConf)
//服务延迟结束
defer server.Stop()
//将上下文信息传给ctx
ctx := svc.NewServiceContext(c)
//将http请求和上下文信息发给拦截器
handler.RegisterHandlers(server, ctx)
fmt.Printf("Starting server at %s:%d...\n", c.Host, c.Port)
server.Start()
} - 可以看到入口程序会先将配置文件加载进来,接着根据配置文件会获取一个server,server里面会包含http请求路径和参数等,之后会调用svc目录下的servercontext.go文件中的函数NewServiceContext,我们需要着重看这个文件,如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15package svc
import (
"hello/internal/config"
)
//这里配置的是上下文可能用到的功能,例如配置文件,数据库,redis,消息队列等,这里只展示了配置文件
type ServiceContext struct {
Config config.Config
}
// 这个函数会将这些功能返回到主函数中,再由主函数将获取的上下文功能传给hander
func NewServiceContext(c config.Config) *ServiceContext {
return &ServiceContext{
Config: c,
}
} - 在主函数中获取到了上下文的对象(里面包含了可能用到的功能),并将上下文对象和server传给了路由(routes.go),由路由转发拦截器
接着看路由:可以看到主函数中调用的就是路由中的函数,我这里只写了一个接口,所有只有一个请求路径,当满足这个路径时,会调用拦截器里的函数。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22package handler
import (
"net/http"
"hello/internal/svc"
"github.com/zeromicro/go-zero/rest"
)
func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
server.AddRoutes(
[]rest.Route{
{
Method: http.MethodGet,
//路径,修改路径在types.go文件中
Path: "/from/:name",
Handler: HelloHandler(serverCtx),
},
},
)
}
拦截器:可以看到在拦截器中,会将请求信息和上下文信息都传给了业务逻辑层的函数。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31package handler
import (
"net/http"
"github.com/zeromicro/go-zero/rest/httpx"
"hello/internal/logic"
"hello/internal/svc"
"hello/internal/types"
)
func HelloHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
//获取请求变量
var req types.Request
// 如果请求有误,则返回空
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
return
}
// 将上下文对象和请求信息传给业务逻辑
l := logic.NewHelloLogic(r.Context(), svcCtx)
// 获取业务逻辑处理后的结果
resp, err := l.Hello(&req)
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.OkJsonCtx(r.Context(), w, resp)
}
}
}
业务逻辑函数(logic目录下):至此整个http的请求过程就完毕了1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32package logic
import (
"context"
"hello/internal/svc"
"hello/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type HelloLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewHelloLogic(ctx context.Context, svcCtx *svc.ServiceContext) *HelloLogic {
return &HelloLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *HelloLogic) Hello(req *types.Request) (resp *types.Response, err error) {
// todo: add your logic here and delete this line
// 这里是具体的逻辑,例如增删改查数据库,redis等
resp = new(types.Response)
resp.Message = req.Name
return
}
goctl-model
goctl-model 可以帮我们快速生成对单表的增删改查,类似于mybatis-plus。
执行以下命令即可生成big_event数据库中user表的增删改查
1 | |
效果:

goctl 一键安装环境
使用以下命令可以一键安装缺少的依赖等,但是goctl版本要大于1.3.3
1 | |

gorm
gorm框架类似于JAVA中的mybatis-plus,是用于操作数据库的
- zero整合gorm
- 安装grom
1
2
3go get -u gorm.io/gorm
go get -u gorm.io/driver/mysql - yaml文件加入数据库连接池,并导入到配置文件中
- 在svc目录下,上下文文件中加入gorm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16type ServiceContext struct {
Config config.Config
Model model.UserModel
//上下文添加gorm类型变量,方便使用
DB *gorm.DB
}
func NewServiceContext(c config.Config) *ServiceContext {
var db *gorm.DB
//进行连接数据库
db, _ = gorm.Open(mysql.Open(c.DB.DataSource), &gorm.Config{})
return &ServiceContext{
Config: c,
DB: db,
}
}
- 安装grom
- 在handler获取DB,进行增删改查
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16// 定义一个用户变量
var user models.User
// 打印更详细的查询信息
result := l.svcCtx.DB.Table("user").Where("id = ?", req.Id).First(&user)
l.Logger.Infof("查询结果: %+v", user)
if result.Error != nil {
l.Logger.Errorf("查询失败: %v", result.Error)
return nil, result.Error
}
resp = &types.Response{
Id: user.Id, // 确保 models.User 结构体中的字段名与数据库字段对应
Username: user.Username,
Password: user.Password,
}
Etcd
etcd是用于go微服务的服务注册和服务发现功能,他更像是一个加强版的redis。
一般我们会将rpc服务注册到etcd中,然后api接口对前端调用,在同api调用rpc服务。
先来看一个简单的rpc服务:
- 我们先通过proto文件快速创建通过这个文件可快速生成一个由id查询用户的rpc服务,以下是生成的目录结构:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23syntax = "proto3";
package user;
option go_package = "./user";
message IdRequest {
string id = 1;
}
message UserResponse {
// 用户id
string id = 1;
// 用户名称
string username = 2;
}
service User {
rpc getUser(IdRequest) returns(UserResponse);
}
// goctl rpc protoc user/rpc/user.proto --go_out=user/rpc/types --go-grpc_out=user/rpc/types --zrpc_out=user/rpc/
- rpc服务和普通的api服务一样,再yaml文件中添加mysql的连接配置,接着注册到config文件里,再添加到svc上下文中,并在逻辑文件里写入根据id查询数据并返回即可
yaml文件:查询数据逻辑:1
2
3
4
5
6
7
8Name: user.rpc
ListenOn: 0.0.0.0:8080
Etcd:
Hosts:
- 127.0.0.1:2379
Key: user.rpc
Mysql:
DataSource: root:12345@tcp(127.0.0.1:3306)/big_event?charset=utf8mb4&parseTime=True&loc=Local1
2
3
4
5
6
7
8
9func (l *GetUserLogic) GetUser(in *user.IdRequest) (*user.UserResponse, error) {
// todo: add your logic here and delete this line
var resq user.UserResponse
l.svcCtx.DB.Table("user").Select("id,username").Where("id=?", in.Id).First(&resq)
return &user.UserResponse{
Id: resq.Id,
Username: resq.Username,
}, nil
} - api调用rpc
yaml:1
2
3
4
5
6
7
8
9
10Name: video
Host: 0.0.0.0
Port: 8888
UserRpc:
Etcd:
Hosts:
- 127.0.0.1:2379
Key: user.rpc
Mysql:
DataSource: root:12345@tcp(127.0.0.1:3306)/big_event?charset=utf8mb4&parseTime=True&loc=Local
- 同样config中加入rpc
1
2
3
4
5
6
7
8
9
10
11
12
13
14package config
import (
"github.com/zeromicro/go-zero/rest"
"github.com/zeromicro/go-zero/zrpc"
)
type Config struct {
rest.RestConf
UserRpc zrpc.RpcClientConf
Mysql struct {
DataSource string
}
} - svc中加入rpc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23package svc
import (
"github.com/zeromicro/go-zero/zrpc"
"gorm.io/gorm"
"project/user/rpc/userclient"
"project/video/api/internal/config"
)
type ServiceContext struct {
Config config.Config
UserRpc userclient.User
}
func NewServiceContext(c config.Config) *ServiceContext {
return &ServiceContext{
Config: c,
UserRpc: userclient.NewUser(zrpc.MustNewClient(c.UserRpc)),
}
} - 接着就是再逻辑文件中调用rpc即可最后同时启动rpc和api服务,然后调用api接口即可
1
2
3
4
5
6
7
8
9
10
11func (l *GetVideoLogic) GetVideo(req *types.VideoReq) (resp *types.VideoRes, err error) {
// todo: add your logic here and delete this line
user1, err := l.svcCtx.UserRpc.GetUser(l.ctx, &user.IdRequest{
Id: req.Id,
})
fmt.Println(resp, err)
return &types.VideoRes{
Id: req.Id,
Username: user1.Username,
}, nil
}
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Little Monste'Blog!
评论





