【go-zero】快速上手

  • ~4.51K 字
  1. 1. 环境配置
  2. 2. 项目创建
    1. 2.1. 从零开始
    2. 2.2. 已有文件生成
    3. 2.3. 重新生成已有文件
  3. 3. API服务
    1. 3.1. 编写API DSL
    2. 3.2. 生成服务
    3. 3.3. 实现逻辑
    4. 3.4. 运行测试
  4. 4. RPC服务
    1. 4.1. 创建项目
    2. 4.2. 实现逻辑
    3. 4.3. 配置运行
    4. 4.4. 从api服务中调用

环境配置

1
2
3
4
5
6
7
8
# 1. 安装 goctl(代码生成工具)
go install github.com/zeromicro/go-zero/tools/goctl@latest

# 2. 脚手架生成一个 API 服务
goctl api new greet

# 3. 拉取依赖并运行
cd greet && go mod tidy && go run greet.go -f etc/greet-api.yaml

在浏览器中打开 http://localhost:8888/from/world,即可看到响应

项目创建

从零开始

1
2
3
4
5
6
7
# HTTP API 服务
goctl api new myservice
cd myservice && go mod tidy

# gRPC 服务
goctl rpc new myservice
cd myservice && go mod tidy

已有文件生成

若已有.api或.proto文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 从 .api 文件
mkdir myservice && cd myservice
go mod init myservice
goctl api go -api myservice.api -dir .
go mod tidy

# 从 .proto 文件
mkdir myservice && cd myservice
go mod init myservice
goctl rpc protoc myservice.proto \
--go_out=./pb \
--go-grpc_out=./pb \
--zrpc_out=.
go mod tidy

重新生成已有文件

修改文件后重新生成而不覆盖业务逻辑:

1
2
3
4
5
6
7
8
# API:重新生成,保留 internal/logic/
goctl api go -api myservice.api -dir .

# RPC:重新生成,保留 internal/logic/
goctl rpc protoc myservice.proto \
--go_out=./pb \
--go-grpc_out=./pb \
--zrpc_out=.

API服务

以一个用户服务为例

编写API DSL

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
syntax = "v1"

type (
LoginReq {
Username string `json:"username"`
Password string `json:"password"`
}

LoginResp {
Token string `json:"token"`
}

UserInfoReq {
Id int64 `path:"id"`
}

UserInfoResp {
Id int64 `json:"id"`
Username string `json:"username"`
Email string `json:"email"`
}
)

service user-api {
@handler Login
post /user/login (LoginReq) returns (LoginResp)

@handler GetUserInfo
get /user/:id (UserInfoReq) returns (UserInfoResp)
}

生成服务

使用命令生成服务:

1
2
3
goctl api go -api user.api -dir .
go mod init user-api
go mod tidy

生成的结构如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
user-api/
├── etc/
│ └── user-api.yaml
├── internal/
│ ├── config/config.go
│ ├── handler/
│ │ ├── loginhandler.go # 自动生成,不要编辑
│ │ ├── getuserinfohandler.go # 自动生成,不要编辑
│ │ └── routes.go
│ ├── logic/
│ │ ├── loginlogic.go # ← 在这里实现
│ │ └── getuserinfologic.go # ← 在这里实现
│ ├── svc/servicecontext.go
│ └── types/types.go # 从 DSL 自动生成
└── user.go

若在DSL变更后重新申城,直接调用goctl api go -api user.api -dir .,goctl不会覆盖logic/文件

实现逻辑

在internal/logic中实现业务逻辑

编辑loginlogic.go

1
2
3
4
5
6
7
8
func (l *LoginLogic) Login(req *types.LoginReq) (resp *types.LoginResp, err error) {
if req.Username == "admin" && req.Password == "123456" {
return &types.LoginResp{
Token: "mock-jwt-token-for-" + req.Username,
}, nil
}
return nil, errors.New("invalid username or password")
}

编辑getuserinfologic.go

1
2
3
4
5
6
7
func (l *GetUserInfoLogic) GetUserInfo(req *types.UserInfoReq) (resp *types.UserInfoResp, err error) {
return &types.UserInfoResp{
Id: req.Id,
Username: "alice",
Email: "alice@example.com",
}, nil
}

运行测试

使用go run user.go运行,并通过测试工具测试

在postman中填入raw json,发送后可以收到200 OK,并读到我们的token

注意,标签是json时需要自己写raw json,而不是填query参数

postman

RPC服务

创建项目

1
2
3
goctl rpc new user
cd user
go mod tidy

生成服务骨架如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
user/
├── etc/
│ └── user.yaml
├── internal/
│ ├── config/config.go
│ ├── logic/
│ │ └── getuserlogic.go # ← 在这里实现
│ ├── server/userserver.go # gRPC 服务端适配器
│ └── svc/servicecontext.go
├── user/
│ └── user.pb.go # 生成的 protobuf 类型
│ └── user_grpc.pb.go # 生成的 gRPC 桩代码
├── user.go # 主入口
└── user.proto # 源定义

并且会自动生成protobuf文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
syntax = "proto3";

package user;
option go_package="./user";

message Request {
string ping = 1;
}

message Response {
string pong = 1;
}

service User {
rpc Ping(Request) returns(Response);
}

若protobuf变更后重新生成,使用

1
2
3
4
goctl rpc protoc user.proto \
--go_out=./user \
--go-grpc_out=./user \
--zrpc_out=.

实现逻辑

编辑internal/logic文件夹下的文件实现逻辑

1
2
3
4
5
6
func (l *GetUserLogic) GetUser(in *user.GetUserRequest) (*user.GetUserResponse, error) {
return &user.GetUserResponse{
Id: in.Id,
Name: "alice",
}, nil
}

配置运行

配置文件位于etc/user.yaml,可以看到rpc服务名称、监听端口等

1
2
Name: user.rpc
ListenOn: 0.0.0.0:8080

使用go run user.go 运行,通过接口测试工具测试

postman

从api服务中调用

使用以下指令生成api网关使用的RPC客户端桩代码

1
2
3
4
goctl rpc protoc user.proto \
--go_out=./user \
--go-grpc_out=./user \
--zrpc_out=./client

然后在servicecontext.go中导入client包

1
2
3
4
type ServiceContext struct {
Config config.Config
UserRpc userclient.User // 从生成的 client 包中引入
}

这样就可以在业务逻辑中像调用本地方法一样发起rpc调用

1
resp, err := l.svcCtx.UserRpc.GetUser(l.ctx, &user.GetUserRequest{Id: 1})

且可以自动处理连接池、负载均衡和熔断

赞助喵
非常感谢您的喜欢!
赞助喵