快速入门
本章从零开始,带你走完 go-zero 的 Hello World 和项目搭建流程。
项目布局
go-zero 推荐 Monorepo 风格,按业务领域横向拆分:
mall/ # 工程根目录
├── go.mod
├── go.sum
├── common/ # 公共库(错误码、中间件、工具函数)
│ ├── xerr/
│ └── xhttp/
└── service/ # 所有微服务
├── user/
│ ├── api/ # HTTP 对外服务
│ ├── rpc/ # gRPC 内部服务
│ └── model/ # 数据模型层
├── order/
│ ├── api/
│ ├── rpc/
│ └── model/
└── payment/
└── rpc/💡 每个业务域(user/order/payment)可包含 api、rpc、model、cronjob、rmq(消息队列消费者)等子模块。
Hello World
1. 创建项目目录
mkdir mall && cd mall
go mod init github.com/yourorg/mall2. 生成 API 服务脚手架
mkdir -p service/greet && cd service/greet
goctl api new greet
cd greet
go mod tidy🚨 注意:
go mod init必须使用完整路径(如github.com/yourorg/mall),短名称会导致 import 解析失败。
3. 查看生成的项目结构
greet/
├── etc/
│ └── greet-api.yaml # 配置文件(端口、日志等)
├── internal/
│ ├── config/
│ │ └── config.go # 配置结构体
│ ├── handler/
│ │ ├── greethandler.go # HTTP handler(自动生成,不编辑)
│ │ └── routes.go # 路由注册(自动生成)
│ ├── logic/
│ │ └── greetlogic.go # ← 你的业务逻辑在这里!
│ ├── svc/
│ │ └── servicecontext.go # 共享依赖(DB、Redis 等注入点)
│ └── types/
│ └── types.go # 请求/响应结构体(自动生成)
└── greet.go # 主入口4. 编写业务逻辑
打开 internal/logic/greetlogic.go,替换 Greet 方法:
func (l *GreetLogic) Greet(req *types.Request) (resp *types.Response, err error) {
return &types.Response{
Message: "Hello " + req.Name,
}, nil
}5. 运行服务
go run greet.go
# 输出: Starting server at 0.0.0.0:8888...6. 测试
curl http://localhost:8888/from/you
# 返回: {"message":"Hello you"}请求流转过程
curl /from/alice
→ routes.go (路由匹配)
→ greethandler.go (解析 + 校验请求)
→ greetlogic.go (你的业务逻辑)
→ greethandler.go (序列化响应)
→ {"message":"Hello alice"}构建第一个 API 服务:用户登录
1. 编写 API DSL
mkdir -p service/user/api && cd service/user/api创建 user.api:
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)
}2. 生成代码
goctl api go -api user.api -dir .
go mod tidy生成的目录结构:
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.go3. 实现业务逻辑
internal/logic/loginlogic.go:
func (l *LoginLogic) Login(req *types.LoginReq) (resp *types.LoginResp, err error) {
// 生产环境:对照数据库验证凭据,签发真实 JWT
if req.Username == "admin" && req.Password == "123456" {
return &types.LoginResp{
Token: "Bearer " + req.Username,
}, nil
}
return nil, errors.New("invalid credentials")
}internal/logic/getuserinfologic.go:
func (l *GetUserInfoLogic) GetUserInfo(req *types.UserInfoReq) (resp *types.UserInfoResp, err error) {
// 生产环境:通过 l.svcCtx 查询数据库或调用 RPC
return &types.UserInfoResp{
Id: req.Id,
Username: "alice",
Email: "alice@example.com",
}, nil
}4. 运行和测试
go run user.go创建 user.http 文件:
POST http://localhost:8888/user/login
Content-Type: application/json
{
"username":"admin",
"password":"123456"
}
# 结果: {"token":"Bearer admin"}
###
GET http://localhost:8888/user/1
# 结果: {"id":1,"username":"alice","email":"alice@example.com"}5. 修改 DSL 后重新生成
goctl api go -api user.api -dir .💡 goctl 只会覆盖它管理的文件(
handler/、types/、routes.go)。你的logic/文件会被保留,不会被覆盖。
构建第一个 RPC 服务
1. 创建 proto 文件
mkdir -p service/user/rpc && cd service/user/rpc创建 user.proto:
syntax = "proto3";
package rpc;
option go_package = "./rpc";
service User {
rpc GetUser(GetUserRequest) returns (GetUserResponse);
}
message GetUserRequest {
int64 id = 1;
}
message GetUserResponse {
int64 id = 1;
string name = 2;
}2. 生成代码
goctl rpc protoc user.proto \
--go_out=./ \
--go-grpc_out=./ \
--zrpc_out=./
go mod tidy项目结构:
user/
├── etc/
│ └── user.yaml # RPC 配置
├── internal/
│ ├── config/config.go
│ ├── logic/
│ │ └── getuserlogic.go # ← 在这里实现
│ ├── server/userserver.go # gRPC 服务端适配器
│ └── svc/servicecontext.go
├── rpc/
│ ├── user.pb.go # protobuf 类型
│ └── user_grpc.pb.go # gRPC 桩代码
├── user.go # 主入口
└── user.proto # 源定义3. 实现 RPC 逻辑
func (l *GetUserLogic) GetUser(in *rpc.GetUserRequest) (*rpc.GetUserResponse, error) {
return &rpc.GetUserResponse{
Id: in.Id,
Name: "alice",
}, nil
}4. 配置并运行 RPC
etc/user.yaml:
Name: user.rpc
ListenOn: 0.0.0.0:8080
Etcd:
Hosts:
- 127.0.0.1:2379
Key: user.rpc🚨 必须先启动 etcd,否则 RPC 服务启动会失败。如果不想依赖 etcd,可使用直连模式(删除
Etcd配置块即可)。
go run user.goAPI 调用 RPC:打通全链路
1. API 配置中添加 RPC 客户端配置
service/user/api/etc/user-api.yaml:
Name: user-api
Host: 0.0.0.0
Port: 8888
UserRpcConf:
Etcd:
Hosts:
- 127.0.0.1:2379
Key: user.rpc若使用直连模式:
UserRpc: Endpoints: - 127.0.0.1:8080
2. 在 Config 中声明 RPC 客户端
service/user/api/internal/config/config.go:
type Config struct {
rest.RestConf
UserRpcConf zrpc.RpcClientConf
}3. 在 ServiceContext 中注入 RPC 客户端
service/user/api/internal/svc/servicecontext.go:
type ServiceContext struct {
Config config.Config
UserRpc user.User // RPC 客户端
}
func NewServiceContext(c config.Config) *ServiceContext {
return &ServiceContext{
Config: c,
UserRpc: user.NewUser(zrpc.MustNewClient(c.UserRpcConf)),
}
}4. 在 Logic 中调用 RPC
func (l *GetUserInfoLogic) GetUserInfo(req *types.UserInfoReq) (resp *types.UserInfoResp, err error) {
res, err := l.svcCtx.UserRpc.GetUser(l.ctx, &user.GetUserRequest{
Id: req.Id,
})
if err != nil {
return nil, err
}
return &types.UserInfoResp{
Id: res.Id,
Username: res.Name,
}, nil
}5. 启动全链路
# 终端1:启动 etcd
etcd
# 终端2:启动 RPC 服务
cd service/user/rpc && go run user.go
# 终端3:启动 API 服务
cd service/user/api && go run user.go# 测试
curl http://localhost:8888/user/1
# 返回: {"id":1,"username":"alice"}🔬 调用链:
curl → API handler → API logic → RPC client → gRPC → RPC server → RPC logic → 返回
本章小结
| 步骤 | 命令 | 产物 |
|---|---|---|
| 创建 API 服务 | goctl api new <name> |
HTTP 服务脚手架 |
| 从 DSL 生成 | goctl api go -api <file> -dir . |
带路由的完整 HTTP 服务 |
| 创建 RPC 服务 | goctl rpc protoc <file> --zrpc_out=. |
gRPC 服务脚手架 |
| 生成 Model | goctl model mysql ddl -c -src <sql> -dir . |
带缓存的 CRUD 代码 |
| 修改后重新生成 | 同第二条命令 | 覆盖 handler/types,保留 logic |