必须通过Kratos Client封装层创建gRPC客户端,而非直接new pb.NewGreeterClient(conn),否则丢失中间件、负载均衡与context跟踪;需在di/provider.go中注册带balancer、interceptor、timeout的client实例,并按直连或服务发现模式正确配置连接。

如何在Kratos中发起gRPC调用:客户端初始化是关键
不能直接 new pb.NewGreeterClient(conn),必须通过 Kratos 的 Client 封装层创建,否则会丢失中间件(如重试、熔断、指标)、无法接入负载均衡、也无法自动注入 context 跟踪信息。
正确做法是在 internal/service/ 或调用方的 internal/xxx/ 目录下,用依赖注入方式获取已配置好的 client 实例:
-
client必须在internal/di/provider.go中注册,例如:provider.RegisterGRPCClient(func() *pb.GreeterClient { return pb.NewGreeterClient(clientConn()) }) -
clientConn()需返回已配置好 balancer、interceptor、timeout 的*grpc.ClientConn,通常由grpc.Dial+grpc.WithBalancerName(p2c.Name)构建 - 若服务端启用了 TLS,客户端需显式传入
grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{})),否则连接失败报connection closed
直连 vs 服务发现:本地调试和线上部署路径不同
开发阶段常用直连,但上线后必须走服务发现,否则无法扩展或容错。
直连写法(仅限本地):
- 在
configs/grpc.toml中配死地址:addr = "127.0.0.1:9001" - 调用方使用
warden.Direct模式解析该地址,不经过 Consul/Etcd - 注意:多个服务共用同一端口会触发
bind: address already in use,务必检查http.address和grpc.address是否全局唯一
服务发现写法(生产必需):
- 确保
registry配置指向统一注册中心(如etcd://127.0.0.1:2379) - 服务端注册时 name 必须全局唯一(如
user.service),否则其他 client 会随机选错实例 - 客户端默认使用
p2c负载均衡,不需要额外配置;若要切回round_robin,需手动指定grpc.WithBalancerName(roundrobin.Name)
调用时传参与错误处理:别忽略 status.Code 和 context 超时
gRPC 返回的 error 不是普通 Go error,必须用 status.Convert(err) 解包才能拿到真实错误码,否则永远看到 rpc error: code = Unknown desc = ...。
常见误操作:
- 直接
if err != nil { log.Println(err) }—— 丢失了Code()和Message()结构化信息 - 没设 context timeout,导致请求卡死在连接或服务端阻塞上(尤其流式接口)
- 对
stream类型方法,忘记调用Recv()循环读取,或未检查io.EOF终止条件
推荐写法:
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
defer cancel()
resp, err := client.SayHello(ctx, &pb.HelloRequest{Name: "foo"})
if err != nil {
s := status.Convert(err)
switch s.Code() {
case codes.NotFound:
return errors.New("user not found")
case codes.InvalidArgument:
return errors.New("name is empty")
default:
return err
}
}
生成 client 代码前必须确认 proto 路径和 package 名严格匹配
执行 kratos proto client api/greeter/greeter.proto 后,若生成的 pb 包里找不到 NewGreeterClient,大概率是 proto 文件放错位置或 package 声明不一致。
必须满足:
-
.proto文件必须放在api/xxx/xxx.proto下(如api/greeter/v1/greeter.proto) -
package名必须与目录深度一致(api/greeter/v1/ → package greeter.v1;) - 若用了
google.api.http注解,必须 import"google/api/annotations.proto",且该文件需在proto_path中可寻址 - 引用
common.proto时,import 路径必须是"api/common/common.proto",且其package必须为kratos.api.common,否则生成的类型无法被识别
这些路径和命名规则一旦出错,生成的 Go 代码要么编译失败,要么运行时 panic 报 missing implementation for Xxx —— 错误信息非常隐晦,排查成本高。


















