go微服务本质是靠目录结构、通信边界和部署粒度共同约束出来的:cmd/按服务隔离、internal/禁止跨包引用、跨服务调用仅限网络层,否则架构必然崩坏。

Go 微服务不是靠框架堆出来的,而是靠目录结构、通信边界和部署粒度一起约束出来的。单个 main.go、共用 internal/handler、把所有配置塞进一个 config.yaml —— 这些做法在 3 个服务以内还能糊弄,超过 5 个就必然出现构建冲突、配置覆盖、跨服务强依赖。
cmd/ 下必须按服务建子目录,且名与二进制一致
这是最常被跳过的硬性隔离点。不拆 cmd/,你就没法做服务级构建、独立发布、CI/CD 分流。
-
go build -o ./bin/auth ./cmd/auth和go build -o ./bin/order ./cmd/order能产出完全独立的可执行文件,互不影响 -
cmd/auth/main.go里只做三件事:加载配置(用viper或envconfig)、调用bootstrap.Init()、监听os.Interrupt信号后优雅退出 - 禁止在
main.go里写业务逻辑、初始化 DB、注册路由——这些全交给bootstrap或internal/auth去做 - CI 流水线可对每个
cmd/xxx单独跑单元测试、集成测试、镜像构建,避免“改一个服务,全量重测”
internal/ 按服务名切分,禁止跨包 import
Go 编译器不会阻止你写 import "myproject/internal/order/service" 在 auth 里,但这是微服务架构崩坏的起点。
- 每个服务在
internal/下有唯一根包:internal/auth、internal/order,各自含handler/、service/、repository/ - 跨服务调用只能走网络:HTTP/gRPC 接口或消息队列,不能通过包引用穿透边界
- 如果两个服务真要共享逻辑(比如统一的 JWT 校验),必须抽到
pkg/authn,且满足:被至少两个服务 import、有单元测试、有版本 tag、能go get - 别把
internal/common当垃圾桶——它只放当前服务内复用的工具函数,不解决跨服务耦合
服务间 HTTP 调用必须封装客户端,带超时、重试、熔断
裸用 http.DefaultClient 是线上事故高发区。下游挂了,你的服务会卡住 goroutine、耗尽连接、拖垮整个链路。
- 每个对外依赖(如调用
order服务)都应封装独立 client 包,例如clients/orderclient - 所有请求必须用
context.WithTimeout(r.Context(), 2*time.Second)控制生命周期,别用固定time.Second * 5 - 仅对幂等操作(GET)做最多 2 次重试;POST/PUT 禁止自动重试,由上层业务决定是否补偿
- 熔断用
gobreaker或简易计数器:连续 5 次失败 → 熔断 30 秒 → 半开状态放 1 个请求试探 - 错误日志必须包含 trace ID、目标地址、耗时、原始 error,否则分布式调试等于盲人摸象
Docker 镜像构建要显式指定服务入口,Kubernetes 部署按服务拆 manifest
容器化不是加个 Dockerfile 就完事。镜像没绑定具体服务,K8s 就没法做资源隔离、弹性伸缩、灰度发布。
-
Dockerfile的CMD必须指向单一服务二进制:CMD ["/app/auth"],而不是["/app/server"] - K8s
Deployment按服务命名:auth-deployment.yaml、order-deployment.yaml,不要合并成一个大 YAML - 每个 Deployment 的
image字段只拉取对应服务镜像,避免“一个服务更新,全部滚动重启” - Liveness/Readiness 探针路径要和服务真实健康检查端点一致,比如
/healthz,别写成通用/
真正难的不是写代码,是每次新增一个 import、加一行 curl、改一个 config.yaml 时,停下来问一句:这会不会悄悄打破服务边界?
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











