优先用 gRPC + 自研注册发现;go-micro v0.40+ 插件化导致文档断层、依赖 etcd/consul 易卡初始化;gRPC 调用必须透传 context 防超时丢失,metadata 须用 AppendToOutgoingContext。

用 go-micro 还是直接上 gRPC?
别一上来就选框架。如果你的微服务只是内部小系统、接口不多、团队就两三人,gRPC + net/http 手写注册发现更轻、更可控。而 go-micro 0.40+ 版本已转向插件化,文档断层、示例过时,新手容易卡在 micro.NewService 初始化失败或 service.Run 启不动——根本原因是它默认依赖本地 etcd 或 consul,但没告诉你得先跑起来。
实操建议:
- 新项目优先用
gRPC定义.proto,生成 server/client,再用registry包(如hashicorp/consul/api)自己做服务注册,逻辑透明 - 若必须用
go-micro,确认版本:v2/v3 不兼容 v1,且 v2 的micro.NewService已移除Address参数,改用server.Address配置项 - 本地调试时,用
consul agent -dev启个单节点,比配etcd省事,且go-micro对 Consul 的 client 兼容性更好
gRPC 服务间传 Context 要小心超时传递
微服务链路里,A → B → C,如果 A 设置了 context.WithTimeout,B 调 C 时没把 context 带过去,C 就收不到超时信号,可能一直 hang 住。这不是 Go 语言 bug,是 gRPC 默认不透传 deadline,除非你显式用 ctx 调用 client.Call(ctx, ...)。
常见错误现象:
立即学习“go语言免费学习笔记(深入)”;
- B 服务日志显示 “waiting for response from C”,但 C 日志根本没收到请求
- C 的 handler 函数里
ctx.Done()永远不触发,select卡死
实操建议:
- 所有 gRPC client 调用必须传原始入参
ctx,不要 new 一个空context.Background() - 需要自定义 metadata(比如 trace-id),用
metadata.AppendToOutgoingContext(ctx, "trace-id", id),不是往 request struct 里塞字段 - 别在 handler 里用
time.Sleep模拟耗时——它不响应ctx.Done(),要用select { case
Docker 部署时 consul 地址写 localhost 必挂
本地跑通的服务,一丢进 Docker 就连不上注册中心,报错:connection refused 或 no route to host。90% 是因为代码里写死了 http://localhost:8500 ——容器里 localhost 指自己,不是宿主机。
使用场景:
- Go 服务和 Consul 分开部署(推荐):Consul 在独立容器或宿主机,Go 服务通过 Docker network 或 host IP 访问
- Consul 和 Go 服务同容器(不推荐):启动顺序难控,Consul 没 ready 就执行注册,失败后不重试
实操建议:
- 用环境变量注入地址:
CONSUL_ADDR=consul:8500(Docker Compose 里 service 名作 hostname),代码里读os.Getenv("CONSUL_ADDR") - Docker Compose 中加
depends_on不保证 readiness,Consul 启动后需额外健康检查,Go 服务启动前可加简单 curl 轮询 - 别信网上“用
host.docker.internal”的方案——Mac/Windows 支持,Linux 默认不开启,CI 环境大概率失效
HTTP 网关和 gRPC 后端混用时,错误码别硬映射
前端只认 HTTP 状态码,但 gRPC 只返回 status.Code(如 codes.NotFound)。有人写一堆 switch 把每个 gRPC code 映射到 HTTP code,结果 codes.Unauthenticated 映成 401 没问题,但 codes.PermissionDenied 也映成 403,导致权限校验失败和资源不存在无法区分。
性能影响:
- 手动映射增加 gateway 层 CPU 开销,尤其高频错误路径
- gRPC status detail 里的 message、details 字段全丢了,debug 时只剩 “Internal Error”
实操建议:
- 用
grpc-gateway自动生成 HTTP 接口,它默认按规范映射:NotFound → 404,InvalidArgument → 400,PermissionDenied → 403,无需手写 - 如果必须自定义(比如要加 trace-id header),在
runtime.WithMetadata回调里取status.FromError(err),再根据Code()和Details()做细粒度处理 - 别把 gRPC error message 直接当 HTTP body 返回——可能含敏感路径或 stack,至少做
strings.Contains(msg, "failed to")这类过滤
真正麻烦的从来不是怎么搭起三个服务,而是服务重启时注册信息没及时注销、跨语言客户端对 metadata 编码理解不一致、还有那个永远在凌晨两点触发的 DNS 缓存超时问题。


















