Skip to content
Go
快速入门

快速入门

本章从零开始,带你走完 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/mall

2. 生成 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.go

3. 实现业务逻辑

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.go

API 调用 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