go-zero 微服务架构入门与实践
在我最初看来,go-zero 最核心的价值体现在两点:
- 使用 .api 定义接口协议(Contract First)
- 使用 goctl 自动生成工程骨架,让开发者专注于业务逻辑
在使用中,我发现 go-zero 的核心并不止于脚手架,而是一整套围绕'可维护性、可扩展性'的工程化约束体系。
后来真正进入项目后,我才逐渐意识到:RPC(zrpc + etcd)才是 go-zero 支撑微服务架构的第二个关键支点。而中间件、熔断、限流、链路追踪,是第三个核心支点。
参考:go-zero 文档
配置环境
安装 goctl(go-zero 的脚手架)
go install github.com/zeromicro/go-zero/tools/goctl@latest
生成代码
方法一:一键生成
等环境配置完毕后,直接进入编辑器,在命令行中输入:
goctl api new firstdemo
骨架将自动搭建完毕。
方法二:手动创建 .api 文件
首先创建一个.api 文件:
syntax = "v1"
info (
title: "gozero-demo"
desc: "first api"
author: "you"
version: "1.0"
)
type(
PingReq {
name string `form:"name,optional"`
}
PingResp {
message string `json:"message"`
}
CreateReq {
title string `json:"title"`
content string `json:"content"`
}
CreateResp {
id int64 `json:"id"`
}
)
service demo-api {
@handler Ping get /ping (PingReq) returns (PingResp)
@handler CreatePost post /posts (CreateReq) returns (CreateResp)
}
说明:
- get/ping:GET 参数通常用 form:""(对应 query)
- post /posts:POST JSON 用 json:""
- @handler Xxx:生成的 handler/logic 名字,即对应的 router、控制器、业务层代码
执行命令:
goctl api go -api demo.api -dir .
生成的目录结构如下:
go_zero_project/
├── demo.api # API 协议定义(接口契约,最核心)
├── demo-api.go # 程序入口(main)
│ └── main() # 启动 HTTP Server
├── etc/
│ └── demo-api.yaml # 配置文件(端口 / DB / Redis / JWT 等)
├── internal/
│ ├── handler/ # HTTP 层(参数 → 逻辑)
│ │ ├── pinghandler.go
│ │ └── createposthandler.go
│ ├── logic/ # 业务逻辑层(核心代码写这里)
│ │ ├── pinglogic.go
│ │ └── createpostlogic.go
│ ├── types/ # 请求 / 响应结构体(由 .api 生成)
│ │ ├── ping.go
│ │ └── createpost.go
│ └── svc/
│ └── servicecontext.go # 依赖注入(DB / Redis / RPC Client)
└── go.mod # Go Module 定义
热加载
下载 air:
go install github.com/air-verse/air@latest
运行:
air
作用:
- 监听文件变化
- 自动 go build
- 杀掉旧进程 → 启动新进程
实现 GET/POST/PUT/DELETE
新建一个 first.api 的文件,依次粘贴以下四种不同类型的定义。
GET 查询
// 获取用户
type GetUserReq {
id int64 `form:"id"`
}
type GetUserResp {
id int64 `json:"id"`
name string `json:"name"`
}
service demo-api {
@handler GetUser get /user (GetUserReq) returns (GetUserResp)
}
POST 创建
// 创建用户
type CreateUserReq {
name string `json:"name"`
age int `json:"age"`
}
type CreateUserResp {
id int64 `json:"id"`
}
service demo-api {
@handler CreateUser post /user (CreateUserReq) returns (CreateUserResp)
}
PUT 更新
// 更新用户
type UpdateUserReq {
id int64 `path:"id"`
name string `json:"name"`
}
type UpdateUserResp {
ok bool `json:"ok"`
}
service demo-api {
@handler UpdateUser put /user/:id (UpdateUserReq) returns (UpdateUserResp)
}
DELETE 删除
// 删除用户
type DeleteUserReq {
id int64 `path:"id"`
}
type DeleteUserResp {
ok bool `json:"ok"`
}
service demo-api {
@handler DeleteUser delete /user/:id (DeleteUserReq) returns (DeleteUserResp)
}
运行命令:
goctl api go -api firstdemo.api -dir .
之后即可得到基础的增删改查功能。
动态路由
只要仅操作一个资源的,都可以考虑用动态路由。
-
路由路径利用
:变量名:get /articles/:id:id表示这里是一个占位符。 -
请求体里用
path:"变量名":type GetArticleReq { id int64 `path:"id"` }切记 path:"id" 必须和 :id 一模一样,否则解析不到值。
-
method + handler 正确绑定:
@handler GetArticle get /articles/:id (GetArticleReq) returns (GetArticleResp)
多个参数
GET /users/:userId/articles/:articleId
type GetUserArticleReq {
userId int64 `path:"userId"`
articleId int64 `path:"articleId"`
}
中间件
什么是中间件?
一条没有中间件的访问路径大致是这样:
HTTP Request ↓ handler ↓ logic ↓ response
但现实项目里,你经常要做些和业务无关、但每个接口都要做的事:
- 校验登录态 / Token
- 打日志
- 统一鉴权(验证权限)
- 限流(限制并发)
- 统计耗时
如果把这些都写进 logic 或 handler,结果是:每个接口重复写、业务逻辑被污染,后期根本维护不动。
挂载上中间件
中间件 = 在请求进入 handler 之前 / 返回响应之前,插一段统一逻辑。
HTTP Request ↓ Middleware(Auth / Log / RateLimit) ↓ handler ↓ logic ↓ response
在框架中的位置
internal/
├── middleware/ ← 中间件目录
│ └── authmiddleware.go
├── handler/ ← HTTP 参数解析
├── logic/ ← 业务逻辑
├── svc/
│ └── servicecontext.go ← 依赖注入(中间件从这拿资源)
记住:中间件 ≠ handler ≠ logic,它是作为一个独立层存在的。
中间件的样子
// Code scaffolded by goctl. Safe to edit.
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) {
// TODO generate middleware implement function, delete after code implementation
// Passthrough to next handler if need
next(w, r)
}
}
如何生成?
新建一个文件:auth.api
syntax = "v1"
type GetUserReq {
id int64 `path:"id"`
}
type GetUserResp {
id int64 `json:"id"`
}
@server (
group: user
middleware: Auth
)
service user-api {
@handler GetUser get /users/:id (GetUserReq) returns (GetUserResp)
}
通过运行:
goctl api go -api auth.api -dir .
@doc 与 import
@doc 是什么?
@doc 是写在路由上的'接口说明元数据',用于生成文档/让接口更可读。
@doc "获取文章详情"
@handler GetArticle get /articles/:id (GetArticleReq) returns (GetArticleResp)
import 是什么?
当你的项目接口多了,一个 .api 文件会越来越大。import 的作用是把 api 文件拆分成多个小文件,再由一个入口 api 汇总。
布局方式:
api/
├── auth.api # 入口(import 其它 api)
├── user.api # 用户相关
└── types.api # 公共 type(可选)
auth.api
syntax = "v1"
import "types.api"
import "user.api"
user.api
syntax = "v1"
@server(
group: user
)
service user-api {
@doc "获取用户详情"
@handler GetUser get /users/:id (GetUserReq) returns (GetUserResp)
}
types.api
syntax = "v1"
type GetUserReq {
id int64 `path:"id"`
}
type GetUserResp {
id int64 `json:"id"`
name string `json:"name"`
}
最后命令行一生成:
goctl api go -api auth.api -dir .
RPC 服务
首先,我们要先明确:RPC = 远程函数调用(像本地函数一样调用远程服务)。但注意!RPC 的核心不是'远程',而是:
- 强接口约束(proto)
- 高性能(HTTP/2 + protobuf)
- 面向服务而不是面向资源
在 go-zero 里:
API 服务 ——(rpc client)——> RPC 服务 ——> 业务逻辑 / DB
在 rpc 与 go-zero 集合的项目中,往往少不了以下这四步:
*.proto→ 接口契约(定义有哪些方法、参数、返回值)goctl rpc new / protoc→ 生成 RPC 服务骨架etc/*.yaml→ RPC 监听端口、服务名internal/logic→ 你真正写业务的地方
大家可以手动创建一个 user 的包,输入:
goctl rpc new
你将会得到:
user/
├── user.proto
├── user.go
├── etc/
│ └── user.yaml
├── internal/
│ ├── config/
│ ├── logic/
│ ├── server/
│ └── svc/
└── go.mod
这样一个骨架。此时你会看到,这样一份协议:
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);
}
与 user.go 中的启动函数。
package main
import (
"flag"
"fmt"
"go_zero_project/gozero-rpc-demo/user/internal/config"
"go_zero_project/gozero-rpc-demo/user/internal/server"
"go_zero_project/gozero-rpc-demo/user/internal/svc"
"go_zero_project/gozero-rpc-demo/user/user"
"github.com/zeromicro/go-zero/core/conf"
"github.com/zeromicro/go-zero/core/service"
"github.com/zeromicro/go-zero/zrpc"
"google.golang.org/grpc"
"google.golang.org/grpc/reflection"
)
var configFile = flag.String("f", "etc/user.yaml", "the config file")
func main() {
flag.Parse()
var c config.Config
conf.MustLoad(*configFile, &c)
ctx := svc.NewServiceContext(c)
s := zrpc.MustNewServer(c.RpcServerConf, func(grpcServer *grpc.Server) {
user.RegisterUserServer(grpcServer, server.NewUserServer(ctx))
if c.Mode == service.DevMode || c.Mode == service.TestMode {
reflection.Register(grpcServer)
}
})
defer s.Stop()
fmt.Printf("Starting rpc server at %s...\n", c.ListenOn)
s.Start()
}
在终端中输入 go mod tidy,更新拉取一下,run 一下,即可启动。
etcd
如果运行失败了,大概率是 etcd 尚未启动,这是新手最常见的问题之一。
什么是 etcd? etcd 你可以将其当成一个高可用的分布式 KV 存储(Key-Value),核心用途在微服务里是:
- 服务发现:服务启动把自己的地址写进去(注册)
- 配置中心/开关:把配置、feature flag 放进去
- 分布式协调:租约、锁、选主....
如果你是第一次接触 etcd,可以先'功能上'把它理解为:一个专门给微服务用的「注册中心 / 配置中心」。
注意:
- etcd ≠ Redis
- etcd 具有强一致(Raft)
- Redis 更偏缓存、高吞吐
一、下载
下载地址:https://github.com/etcd-io/etcd/releases
二、配置
把你含有以下两个文件的 cmd 路径,配置到全局 PATH 路径内:
- etcd.exe
- etcdctl.exe
三、运行
直接在 cmd 内,输入以下四个英文单词:
etcd
启动!
四、检测
netstat -ano | findstr :2379
看一看是否在监听!
此时,你在回去运行一下项目,自然就跑通了。
五、为什么要用这个?
看 etc 内的配置文件:
Name: user.rpc
ListenOn: 0.0.0.0:8080
Etcd:
Hosts:
- 127.0.0.1:2379
Key: user.rpc
因为配置文件用到了 etcd。
调用 RPC 服务
初步调用
首先启动服务。之后在 apifox 中操作的,创建基于 grpc 的接口管理!(当然你换成其他也操作函数)然后把我的.proto 文件放置进去。在 user.User /Ping 接口上,点击调用。
{"pong":"hello"}
修改
插入这些,然后在 service User 里加一条:方法名:GetUser,请求:GetUserRequest,响应:GetUserResponse,并新增 2 个 message:
syntax = "proto3";
package user;
option go_package="./user";
message Request {
string ping = 1;
}
message Response {
string pong = 1;
}
message GetUserRequest {
int64 id = 1;
}
message GetUserResponse {
int64 id = 1;
string name = 2;
}
service User {
rpc Ping(Request) returns(Response);
rpc GetUser(GetUserRequest) returns(GetUserResponse);
}
最关键的一步:
goctl rpc protoc user.proto --go_out=. --go-grpc_out=. --zrpc_out=.
同个这个,生成 rpc 代码框架。这里就是微服务中,被远程调用部分。也是 go-zero 中的,两个最重要的核心之一!而另一个就是 api,也就是对外!同时调用 rpc 的部分!
API
其实这个主要就两个步骤
一、生成 API 项目骨架
(一般,新启一个项目时用)
goctl api new userapi
二、写 .api 协议后生成代码
(更新项目时使用)
goctl api go -api userapi.api -dir .
其实 api 还有很多组件需要掌握:gorm、auth、jwt…但是这些其实都与 gin 框架大差不差。
这次就先记录到这里,如果想要了解更多、更详细的,可以直接到官网了解,文档写的还是非常简洁通透的:go-zero 文档


