go-micro v4 已弃用 protoc-gen-micro,需改用 protoc-gen-go-grpc 和 protoc-gen-go 生成标准 grpc 接口,再手动集成 client/server;默认注册中心为内存型 mdns,跨环境须显式配置 etcd;gin 与 go-micro 混用时需桥接 context 并透传 metadata;service.run() 卡住多因漏调或重复调用 service.init()。

go-micro v4 用不了 proto-gen-micro?别硬配,换插件
go-micro 自 v4 起彻底移除了 protoc-gen-micro 插件支持,强行安装或沿用旧教程会报 unknown flag: --micro_out 或生成空文件。这不是你 protoc 没装对,是框架本身已弃用该机制。
实操建议:
- v4+ 正确路径是用
protoc-gen-go-grpc+protoc-gen-go生成标准 gRPC 接口,再手动接入 go-micro 的client和server; - 若坚持用 micro 风格的客户端封装(如
proto.NewUserService(...)),需自己写薄层包装器,或降级到 v3.10.x(但 v3 不兼容 Go 1.21+ 的 module 行为); - 推荐方案:直接用
google.golang.org/grpc原生 client/server,再通过 go-micro 的registry和selector做服务发现——这样既可控,又避开已废弃的代码生成链。
注册中心不配 Consul 就 panic?默认行为早变了
v4 默认注册中心从 Consul 改为内存注册(registry=mdns),但很多文档仍按老版本写“不启 Consul 就连不上”。实际现象是:服务能跑、能互相调用,但一重启就失联,日志里没报错,只在客户端调用时卡住或返回 service not found。
原因在于:mdns 只适用于单机多进程调试,跨机器、容器或 CI 环境下完全失效。
实操建议:
- 开发阶段可保留
--registry=mdns,但必须加--registry_address=192.168.x.x:5353显式指定网卡,否则 macOS/Linux 可能绑定失败; - 测试/预发环境务必切到
--registry=etcd --registry_address=http://etcd:2379(注意 etcd v3 API 地址格式是http://,不是etcd://); - 别信“go-micro 自动 fallback”——它不会 fallback,只会静默用内存注册,然后让你花半天查网络策略。
gin 和 go-micro 混用时 context 传丢了?别直接用 gin.Context
常见错误是把 gin 的 *gin.Context 直接塞进 go-micro 的 RPC handler,结果 ctx.Value(...) 全是 nil,中间件注入的 traceID、user.ID 全断掉。
根本原因是:gin.Context 和 go-micro 的 context.Context 完全无关,前者是 HTTP 生命周期,后者是 RPC 调用链上下文。
实操建议:
- 在 gin handler 里调用 go-micro client 时,用
micro.WithContext(ctx, r.Context())显式桥接(r是 *gin.Context); - 若需透传 HTTP header(如
X-Request-ID),得手动提取再塞进 metadata:metadata := make(map[string]string); metadata["x-request-id"] = r.GetHeader("X-Request-ID"),再传给 client; - 别图省事用
context.Background()—— 这会导致超时、重试、链路追踪全部失效。
service.Run() 卡住不动?检查是不是漏了 Init() 或重复调用
典型现象:程序启动后无任何日志、CPU 占用为 0、curl 也连不上,但进程还在。90% 是 service.Init() 被注释、漏写,或在 service.Run() 后又调了一次 service.Init()。
v4 的 Init() 不再是空函数,它会加载配置、初始化 registry、设置信号监听——没它,Run() 就只是个空循环。
实操建议:
- 确保
service.Init()在Run()前且仅执行一次; - 如果用了
micro.Config(...)或micro.Registry(...),这些选项必须传给micro.NewService(),不能靠Init()后补; - 加一行日志验证:
log.Printf("service name: %s", service.Name()),放在Init()后、Run()前——没输出就是 Init 没走成。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











