Skip to content

Instantly share code, notes, and snippets.

@kevwan
Last active November 1, 2025 03:21
Show Gist options
  • Select an option

  • Save kevwan/d78b893e32f85c7dc87b877ec77fd92a to your computer and use it in GitHub Desktop.

Select an option

Save kevwan/d78b893e32f85c7dc87b877ec77fd92a to your computer and use it in GitHub Desktop.
Quickstart tutorial of go-zero project

go-zero 快速入门教程

简介

go-zero 是一个集成了各种工程实践的 web 和 rpc 框架。通过弹性设计保障了大并发服务端的稳定性,经受了充分的实战检验。go-zero 包含极简的 API 定义和生成工具 goctl,可以根据定义的 api 文件一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码,并可直接运行。

核心特性

  • 强大的工具支持:尽可能少的代码编写
  • 极简的接口:完全兼容 net/http
  • 高性能:面向故障编程,弹性设计
  • 内建服务发现、负载均衡
  • 内建限流、熔断、降载:且自动触发,自动恢复
  • API 参数自动校验
  • 超时级联控制
  • 自动缓存控制
  • 链路跟踪、统计报警
  • 高并发支撑:稳定保障了疫情期间每天的流量洪峰

环境准备

1. 安装 Go 语言环境

确保你的系统已经安装了 Go 1.21 或更高版本:

go version

2. 安装 goctl 工具

goctl 是 go-zero 的代码生成工具,可以快速生成项目代码:

# 方式一:通过 go install 安装(推荐)
go install github.com/zeromicro/go-zero/tools/goctl@latest

# 方式二:通过下载二进制文件安装
# 访问 https://github.com/zeromicro/go-zero/releases 下载对应平台的 goctl

# 验证安装
goctl --version

3. 安装 protoc 和 protoc-gen-go(可选,用于 RPC 开发)

# 安装 protoc
# macOS
brew install protobuf

# 安装 protoc-gen-go
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest

快速开始:创建第一个 API 服务

1. 使用 goctl 快速创建项目

# 创建项目目录
mkdir quickstart && cd quickstart

# 使用 goctl 快速创建项目
goctl quickstart

按照提示输入:

  • 选择服务类型:选择 api (HTTP 服务)
  • 输入服务名称:例如 user
  • 输入模块名称:例如 quickstart

2. 项目结构说明

生成的项目结构如下:

user/
├── etc/             # 配置文件目录
│   └── user.yaml    # 服务配置文件
├── internal/        # 内部代码,不对外暴露
│   ├── config/      # 配置定义
│   ├── handler/     # 路由和handler定义
│   ├── logic/       # 业务逻辑
│   ├── svc/         # 服务依赖
│   └── types/       # 请求响应类型定义
├── user.api         # API定义文件
└── user.go          # 程序入口

3. 定义 API

编辑 user.api 文件,定义你的 API 接口:

syntax = "v1"

info (
    title: "用户服务API"
    desc: "用户服务的API接口"
    author: "your-name"
    email: "[email protected]"
    version: "v1"
)

type (
    // 登录请求
    LoginRequest {
        Username string `json:"username"`
        Password string `json:"password"`
    }

    // 登录响应
    LoginResponse {
        Id       int64  `json:"id"`
        Username string `json:"username"`
        Token    string `json:"token"`
    }

    // 用户信息请求
    GetUserRequest {
        Id int64 `path:"id"`
    }

    // 用户信息响应
    GetUserResponse {
        Id       int64  `json:"id"`
        Username string `json:"username"`
        Email    string `json:"email"`
    }
)

// 不需要鉴权的接口
@server (
    prefix: /api/user
)
service user {
    @doc "用户登录"
    @handler login
    post /login (LoginRequest) returns (LoginResponse)
}

// 需要鉴权的接口
@server (
    prefix: /api/user
    jwt: Auth
    middleware: AuthMiddleware
)
service user {
    @doc "获取用户信息"
    @handler getUserInfo
    get /info/:id (GetUserRequest) returns (GetUserResponse)
}

4. 生成代码

# 在项目根目录执行
goctl api go -api user.api -dir .

这个命令会根据 API 定义生成对应的代码结构。

5. 安装 JWT 依赖

在实现业务逻辑之前,我们需要安装 JWT 库来生成真实的 JWT token:

go get -u github.com/golang-jwt/jwt/v4

6. 实现业务逻辑

编辑 internal/logic/loginlogic.go,实现真实的 JWT token 生成:

package logic

import (
    "context"
    "errors"
    "time"

    "quickstart/user/internal/svc"
    "quickstart/user/internal/types"

    "github.com/golang-jwt/jwt/v4"
    "github.com/zeromicro/go-zero/core/logx"
)

type LoginLogic struct {
    logx.Logger
    ctx    context.Context
    svcCtx *svc.ServiceContext
}

func NewLoginLogic(ctx context.Context, svcCtx *svc.ServiceContext) *LoginLogic {
    return &LoginLogic{
        Logger: logx.WithContext(ctx),
        ctx:    ctx,
        svcCtx: svcCtx,
    }
}

func (l *LoginLogic) Login(req *types.LoginRequest) (resp *types.LoginResponse, err error) {
    // 简单的用户名密码验证
    if req.Username == "admin" && req.Password == "123456" {
        // 生成真实的 JWT token
        now := time.Now().Unix()
        accessExpire := l.svcCtx.Config.Auth.AccessExpire

        claims := make(jwt.MapClaims)
        claims["exp"] = now + accessExpire
        claims["iat"] = now
        claims["userId"] = int64(1)

        token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
        tokenString, err := token.SignedString([]byte(l.svcCtx.Config.Auth.AccessSecret))
        if err != nil {
            return nil, err
        }

        return &types.LoginResponse{
            Id:       1,
            Username: req.Username,
            Token:    tokenString,
        }, nil
    }

    return nil, errors.New("用户名或密码错误")
}

JWT Token 说明

生成的 JWT token 包含以下信息:

  • exp:过期时间(从配置文件中的 Auth.AccessExpire 读取)
  • iat:签发时间
  • userId:用户ID(可以添加更多自定义字段,如角色、权限等)

go-zero 的 JWT 中间件会自动:

  1. 从 HTTP Header 的 Authorization 字段获取 token
  2. 使用配置的 AccessSecret 验证 token 签名
  3. 检查 token 是否过期
  4. 验证通过后将请求转发到业务逻辑

编辑 `internal/logic/getuserinfologic.go`:

```go
package logic

import (
    "context"
    "quickstart/user/internal/svc"
    "quickstart/user/internal/types"

    "github.com/zeromicro/go-zero/core/logx"
)

type GetUserInfoLogic struct {
    logx.Logger
    ctx    context.Context
    svcCtx *svc.ServiceContext
}

func NewGetUserInfoLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetUserInfoLogic {
    return &GetUserInfoLogic{
        Logger: logx.WithContext(ctx),
        ctx:    ctx,
        svcCtx: svcCtx,
    }
}

func (l *GetUserInfoLogic) GetUserInfo(req *types.GetUserRequest) (resp *types.GetUserResponse, err error) {
    // 这里实现获取用户信息的逻辑
    // 示例:返回模拟数据
    return &types.GetUserResponse{
        Id:       req.Id,
        Username: "admin",
        Email:    "[email protected]",
    }, nil
}

7. 配置文件

编辑 etc/user.yaml

Name: user
Host: 0.0.0.0
Port: 8888
Timeout: 30000

Auth:
  AccessSecret: your-secret-key-here
  AccessExpire: 7200

Log:
  ServiceName: user
  Mode: console
  Level: info

8. 启动服务

go run user.go -f etc/user.yaml

你会看到类似以下的输出:

Starting server at 0.0.0.0:8888...

9. 测试 API

9.1 测试登录接口

# 测试登录接口 - 成功场景
curl -X POST http://localhost:8888/api/user/login \
  -H "Content-Type: application/json" \
  -d '{"username":"admin","password":"123456"}'

响应示例:

{
  "id": 1,
  "username": "admin",
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MzMwNzEyMDAsImlhdCI6MTczMzA2NDAwMCwidXNlcklkIjoxfQ..."
}

9.2 测试其他场景

# 错误的密码(应返回 400)
curl -X POST http://localhost:8888/api/user/login \
  -H "Content-Type: application/json" \
  -d '{"username":"admin","password":"wrong"}'

# 缺少参数(应返回 400)
curl -X POST http://localhost:8888/api/user/login \
  -H "Content-Type: application/json" \
  -d '{"username":"admin"}'

# 不带 token 访问受保护接口(应返回 401)
curl -X GET http://localhost:8888/api/user/info/1

9.3 测试带 Token 的接口

# 先获取 token
TOKEN=$(curl -s -X POST http://localhost:8888/api/user/login \
  -H "Content-Type: application/json" \
  -d '{"username":"admin","password":"123456"}' | jq -r '.token')

# 使用 token 访问受保护的接口
curl -X GET http://localhost:8888/api/user/info/1 \
  -H "Authorization: $TOKEN"

响应示例:

{
  "id": 1,
  "username": "admin",
  "email": "[email protected]"
}

10. 理解 API 响应和错误码

在 go-zero 中,不同类型的错误会返回不同的 HTTP 状态码:

状态码 场景 说明
200 成功 正常的业务响应
400 参数错误或业务错误 参数验证失败或业务逻辑返回 error
401 未授权 JWT token 缺失或验证失败
404 未找到 路由不存在
405 方法不允许 HTTP 方法与定义不匹配
500 服务器错误 服务内部错误

注意:当业务逻辑返回 error 时,go-zero 默认返回 HTTP 400。如果需要自定义错误码和消息,可以使用自定义错误类型。

创建 RPC 服务

1. 创建 RPC 项目

mkdir user-rpc && cd user-rpc

2. 定义 Proto 文件

创建 user.proto

syntax = "proto3";

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

message GetUserRequest {
  int64 id = 1;
}

message GetUserResponse {
  int64 id = 1;
  string username = 2;
  string email = 3;
}

service User {
  rpc GetUser(GetUserRequest) returns(GetUserResponse);
}

3. 生成 RPC 代码

goctl rpc protoc user.proto --go_out=. --go-grpc_out=. --zrpc_out=.

4. 实现 RPC 逻辑

编辑 internal/logic/getuserlogic.go

package logic

import (
    "context"

    "user-rpc/internal/svc"
    "user-rpc/user"

    "github.com/zeromicro/go-zero/core/logx"
)

type GetUserLogic struct {
    ctx    context.Context
    svcCtx *svc.ServiceContext
    logx.Logger
}

func NewGetUserLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetUserLogic {
    return &GetUserLogic{
        ctx:    ctx,
        svcCtx: svcCtx,
        Logger: logx.WithContext(ctx),
    }
}

func (l *GetUserLogic) GetUser(in *user.GetUserRequest) (*user.GetUserResponse, error) {
    // 实现获取用户逻辑
    return &user.GetUserResponse{
        Id:       in.Id,
        Username: "admin",
        Email:    "[email protected]",
    }, nil
}

5. 启动 RPC 服务

go run user.go -f etc/user.yaml

集成 MySQL 数据库

1. 安装数据库驱动

go get -u gorm.io/gorm
go get -u gorm.io/driver/mysql

2. 修改配置文件

etc/user.yaml 中添加数据库配置:

Name: user
Host: 0.0.0.0
Port: 8888

MySQL:
  DataSource: root:password@tcp(127.0.0.1:3306)/user_db?charset=utf8mb4&parseTime=True&loc=Local

3. 定义数据模型

创建 internal/model/usermodel.go

package model

import (
    "context"
    "database/sql"
    "github.com/zeromicro/go-zero/core/stores/sqlx"
)

var _ UserModel = (*customUserModel)(nil)

type (
    UserModel interface {
        Insert(ctx context.Context, data *User) (sql.Result, error)
        FindOne(ctx context.Context, id int64) (*User, error)
        Update(ctx context.Context, data *User) error
        Delete(ctx context.Context, id int64) error
    }

    customUserModel struct {
        *defaultUserModel
    }

    User struct {
        Id       int64  `db:"id"`
        Username string `db:"username"`
        Password string `db:"password"`
        Email    string `db:"email"`
    }
)

func NewUserModel(conn sqlx.SqlConn) UserModel {
    return &customUserModel{
        defaultUserModel: newUserModel(conn),
    }
}

添加缓存

1. 配置 Redis

etc/user.yaml 中添加 Redis 配置:

Redis:
  Host: 127.0.0.1:6379
  Type: node
  Pass: ""

2. 使用缓存

package logic

import (
    "context"
    "encoding/json"
    "fmt"
    "time"

    "github.com/zeromicro/go-zero/core/stores/redis"
)

func (l *GetUserInfoLogic) GetUserInfo(req *types.GetUserRequest) (resp *types.GetUserResponse, err error) {
    // 从缓存获取
    key := fmt.Sprintf("user:info:%d", req.Id)
    val, err := l.svcCtx.Redis.Get(key)
    if err == nil {
        var user types.GetUserResponse
        json.Unmarshal([]byte(val), &user)
        return &user, nil
    }

    // 从数据库获取
    user, err := l.svcCtx.UserModel.FindOne(l.ctx, req.Id)
    if err != nil {
        return nil, err
    }

    resp = &types.GetUserResponse{
        Id:       user.Id,
        Username: user.Username,
        Email:    user.Email,
    }

    // 写入缓存
    data, _ := json.Marshal(resp)
    l.svcCtx.Redis.Setex(key, string(data), 3600)

    return resp, nil
}

中间件使用

1. 创建自定义中间件

创建 internal/middleware/authmiddleware.go

package middleware

import (
    "net/http"
)

type AuthMiddleware struct {
}

func NewAuthMiddleware() *AuthMiddleware {
    return &AuthMiddleware{}
}

func (m *AuthMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        // 这里实现认证逻辑
        // 例如:验证 JWT token

        // 如果验证通过,继续处理
        next(w, r)

        // 如果验证失败,返回错误
        // http.Error(w, "Unauthorized", http.StatusUnauthorized)
    }
}

2. 注册中间件

internal/svc/servicecontext.go 中注册:

package svc

import (
    "quickstart/user/internal/config"
    "quickstart/user/internal/middleware"

    "github.com/zeromicro/go-zero/rest"
)

type ServiceContext struct {
    Config         config.Config
    AuthMiddleware rest.Middleware
}

func NewServiceContext(c config.Config) *ServiceContext {
    return &ServiceContext{
        Config:         c,
        AuthMiddleware: middleware.NewAuthMiddleware().Handle,
    }
}

常用命令总结

# 创建 API 服务
goctl quickstart

# 从 API 文件生成代码
goctl api go -api user.api -dir .

# 从 Proto 文件生成 RPC 代码
goctl rpc protoc user.proto --go_out=. --go-grpc_out=. --zrpc_out=.

# 从 SQL DDL 生成 Model 代码
goctl model mysql ddl -src user.sql -dir ./model

# 从数据库生成 Model 代码
goctl model mysql datasource -url="user:password@tcp(127.0.0.1:3306)/database" -table="*" -dir="./model"

# 生成 Dockerfile
goctl docker -go user.go

# 生成 Kubernetes 部署文件
goctl kube deploy -name user -namespace default -image user:v1 -o user.yaml -port 8888

# 查看 goctl 版本
goctl --version

# 查看帮助
goctl --help

最佳实践

1. 项目结构建议

project/
├── api/              # API 服务
│   └── user/
├── rpc/              # RPC 服务
│   └── user/
├── model/            # 数据模型(可共享)
├── common/           # 公共代码
│   ├── errorx/      # 错误定义
│   └── utils/       # 工具函数
└── docker-compose.yml

2. 错误处理

创建统一的错误处理:

package errorx

import "fmt"

const (
    OK                  = 0
    ERROR               = 1
    INVALID_PARAMS      = 400
    UNAUTHORIZED        = 401
    FORBIDDEN           = 403
    NOT_FOUND           = 404
    INTERNAL_ERROR      = 500
)

type CodeError struct {
    Code int    `json:"code"`
    Msg  string `json:"msg"`
}

func NewCodeError(code int, msg string) error {
    return &CodeError{Code: code, Msg: msg}
}

func (e *CodeError) Error() string {
    return fmt.Sprintf("Code: %d, Msg: %s", e.Code, e.Msg)
}

3. 响应格式统一

type Response struct {
    Code int         `json:"code"`
    Msg  string      `json:"msg"`
    Data interface{} `json:"data,omitempty"`
}

func Success(data interface{}) *Response {
    return &Response{
        Code: 0,
        Msg:  "success",
        Data: data,
    }
}

func Error(code int, msg string) *Response {
    return &Response{
        Code: code,
        Msg:  msg,
    }
}

4. 日志规范

// 使用结构化日志
logx.WithContext(ctx).Infow("user login",
    logx.Field("username", req.Username),
    logx.Field("ip", r.RemoteAddr),
)

// 记录错误
logx.WithContext(ctx).Errorw("database error",
    logx.Field("error", err.Error()),
    logx.Field("sql", sqlStr),
)

5. 性能优化建议

  • 使用连接池管理数据库连接
  • 合理使用缓存减少数据库查询
  • 开启 gRPC 连接复用
  • 使用批量操作减少网络往返
  • 合理设置超时时间
  • 使用熔断器保护下游服务

部署指南

1. 使用 Docker 部署

# 生成 Dockerfile
goctl docker -go user.go

# 构建镜像
docker build -t user:v1 .

# 运行容器
docker run -d --name user -p 8888:8888 user:v1

2. 使用 Kubernetes 部署

# 生成 k8s 部署文件
goctl kube deploy -name user -namespace default -image user:v1 -o user.yaml -port 8888

# 应用配置
kubectl apply -f user.yaml

3. 使用 Docker Compose

创建 docker-compose.yml

version: '3'

services:
  user-api:
    build: ./api/user
    ports:
      - "8888:8888"
    depends_on:
      - mysql
      - redis
      - user-rpc

  user-rpc:
    build: ./rpc/user
    ports:
      - "9000:9000"
    depends_on:
      - mysql
      - redis

  mysql:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: password
      MYSQL_DATABASE: user_db
    ports:
      - "3306:3306"
    volumes:
      - mysql-data:/var/lib/mysql

  redis:
    image: redis:6-alpine
    ports:
      - "6379:6379"

volumes:
  mysql-data:

常见问题

1. goctl 命令找不到

症状: command not found: goctl

解决方案:

# 确保 GOPATH/bin 在 PATH 中
export PATH=$PATH:$(go env GOPATH)/bin

# 或者重新安装 goctl
go install github.com/zeromicro/go-zero/tools/goctl@latest

2. 端口已被占用

症状: bind: address already in use

解决方案:

# macOS/Linux: 查找占用端口的进程
lsof -i :8888

# 或者修改配置文件中的端口
vim etc/user.yaml
# 将 Port 改为其他值,如 8889

3. JWT token 验证失败

症状: 所有受保护的接口都返回 401

可能原因:

  1. AccessSecret 配置不正确
  2. Token 生成和验证使用的密钥不一致
  3. Token 格式不正确(需要使用 JWT 标准格式)
  4. Token 已过期

解决方案:

# 检查配置文件中的 AccessSecret
cat etc/user.yaml

# 确保代码中使用相同的 AccessSecret
# 检查 loginlogic.go 中的 token 生成代码

4. 参数验证失败

症状: 请求返回 field "xxx" is not set

原因: API 定义中的必填参数未传递

解决方案: 确保请求中包含所有必填参数,参数名称和类型与 API 定义一致

5. 依赖包下载失败

症状: go mod tidygo run 时下载依赖失败

解决方案:

# 设置 Go 代理(中国大陆)
go env -w GOPROXY=https://goproxy.cn,direct

# 或者使用其他代理
go env -w GOPROXY=https://goproxy.io,direct

学习资源

总结

通过本教程,你已经学会了:

  1. ✅ 安装和配置 go-zero 开发环境
  2. ✅ 创建 API 服务和 RPC 服务
  3. ✅ 定义 API 接口和 Proto 文件
  4. ✅ 实现业务逻辑(包括真实的 JWT token 生成)
  5. ✅ 配置和使用 JWT 认证
  6. ✅ 理解 API 响应和错误处理
  7. ✅ 集成数据库和缓存
  8. ✅ 使用中间件
  9. ✅ 项目部署

关键要点

  • JWT 认证: 使用 github.com/golang-jwt/jwt/v4 生成真实的 JWT token
  • 参数验证: go-zero 自动验证 API 定义中的必填参数
  • 错误处理: 业务逻辑返回 error 时默认返回 HTTP 400
  • 中间件: JWT 中间件自动验证 token 的有效性和过期时间

go-zero 提供了完整的微服务开发解决方案,让你可以专注于业务逻辑的实现,而不必担心底层的技术细节。开始构建你的第一个 go-zero 项目吧!

下一步

  • 学习更多关于 API 网关的知识
  • 了解服务治理和监控
  • 探索分布式事务处理
  • 学习性能优化技巧
  • 参与社区贡献

祝你在 go-zero 的学习之旅中取得成功!🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment