目录结构怎么摆、项目怎么组织、代码怎么写。把约定记牢,团队协作和代码生成才顺。
go-zero 生成的服务有固定骨架。无论 API 服务还是 RPC 服务,都遵循「配置 / handler(或 pb) / logic / svc / model」的分层。
| 目录 / 文件 | 职责 | 能不能写业务 |
|---|---|---|
handler / server | 解析请求、调用 logic、组装响应 | ❌ 不要写业务逻辑 |
logic | 真正的业务编排,可调用 RPC / DB / 缓存 | ✅ 业务都在这 |
svc | 装配所有外部依赖(DB、RPC client、配置) | ❌ 只做注入 |
config | 声明 yaml 配置对应的结构体 | — |
types / pb | 数据传输对象,由工具生成 | ❌ 不手改 |
go.mod)或 一服务一仓库。小团队推荐前者,大团队按服务边界拆仓库。一个 go-zero 服务从「配置 → 启动 → 装配依赖 → 跑」有一套固定仪式,别自己发明。
etc/*.yaml,绝不硬编码。conf.MustLoad 加载配置 → NewServer 建服务 → svc.NewServiceContext 装配依赖 → server.Start 启动,并 defer server.Stop()。svc.ServiceContext 上,logic 通过构造函数拿到,不用全局变量。go mod tidy。etc/ 下保留一份可运行的 yaml,提交到仓库作为模板。package main import ( "github.com/zeromicro/go-zero/core/conf" "github.com/zeromicro/go-zero/rest" "user-api/internal/config" "user-api/internal/handler" "user-api/internal/svc" ) var configFile = "etc/user-api.yaml" func main() { var c config.Config conf.MustLoad(configFile, &c) // 1. 加载配置 server := rest.MustNewServer(c.RestConf) // 2. 建 HTTP 服务 defer server.Stop() ctx := svc.NewServiceContext(c) // 3. 装配依赖 handler.RegisterHandlers(server, ctx) // 4. 注册路由 server.Start() // 5. 启动 }
go-zero 风格的几条硬规矩,团队统一后就很少出现「这个逻辑该放哪」的争论。
// ✅ 明确返回错误并带上上下文 if err != nil { return nil, fmt.Errorf("查询用户失败: %w", err) } // ❌ 吞掉错误 if err != nil { return nil, nil }
所有 RPC / DB / 缓存调用都带 ctx,用于超时、链路追踪与取消。logic 的入参第一位是 ctx。
resp, err := l.userRpc.Login(l.ctx, req) // 用注入的 ctx
MySQL / Redis / RPC client 都在 svc 建好,logic 通过 l.svcCtx 取用,避免重复连接。
func (l *LoginLogic) Login(req *types.LoginReq) (*types.LoginResp, error) { u, err := l.svcCtx.UserModel.FindOneByUsername(l.ctx, req.Username) if err != nil { return nil, err } _ = u return &types.LoginResp{Token: "..."}, nil }
type Config struct { rest.RestConf // 内嵌,获得 Host/Port/Timeout 等 UserRpc zrpc.RpcClientConf // 下游 RPC 配置 Mysql sqlx.Config // 数据库 CacheRedis cache.CacheConf // 缓存集群 }
.api 是契约也是文档,写得规范,生成的代码才规范。
// 结构体用大驼峰,字段小驼峰,json tag 同名 type CreateOrderReq { ProductId int64 `json:"productId"` Count int32 `json:"count"` } // 一个 service 聚合一组接口,handler 名用大驼峰动词 service order-api { @handler CreateOrder post /order/create (CreateOrderReq) returns (CreateOrderResp) @handler GetOrder get /order/:id (GetOrderReq) returns (GetOrderResp) }
:id 是路径参数,对应请求结构体的同名字段,框架自动解析。| 对象 | 规则 | 示例 |
|---|---|---|
| 服务 | 小写下划线或中划线,带角色后缀 | user-api / user-rpc |
| 结构体 | 大驼峰 | LoginReq |
| handler | 大驼峰动词 + Handler 后缀 | LoginHandler |
| logic | 大驼峰动词 + Logic 后缀 | LoginLogic |
| 配置文件 | etc/服务名.yaml | etc/user-api.yaml |
| 接口方法 | 大驼峰动词 | CreateOrder |
.api / .proto 重新生成,而不是手改生成物。