没有 .proto 文件时,grpcurl 之类的工具怎么知道服务有哪些方法、请求长什么样?这篇用 grpc-go 的反射服务,自己写了一个迷你版:列服务、查描述符、动态调用。
v1 ListServices:err=<nil>;demo.Demo grpc.health.v1.Health grpc.reflection.v1.ServerReflection grpc.reflection.v1alpha.ServerReflection v1alpha ListServices:err=<nil>/<nil>;demo.Demo grpc.health.v1.Health grpc.reflection.v1.ServerReflection grpc.reflection.v1alpha.ServerReflection 自定义 Services 只含 demo.Demo:err=<nil>;demo.Demo
服务端再注册一个 grpc.reflection 服务,把自己编译进二进制的 .proto 描述告诉客户端。客户端因此不需要 .proto 文件就能列出服务、方法和消息结构。
err=<nil>;返回的文件描述符 1 个
文件 pb/demo.proto(package demo,依赖 [])
Demo.Unary(.demo.Req) returns (.demo.Resp)
Demo.ServerStream(.demo.Req) returns (stream .demo.Resp)
Demo.ClientStream(stream .demo.Req) returns (.demo.Resp)
Demo.Bidi(stream .demo.Req) returns (stream .demo.Resp)
message Req { msg=1(STRING), n=2(INT32), payload=3(BYTES) }
message Resp { msg=1(STRING), n=2(INT32), payload=3(BYTES) }用 FileContainingSymbol 按符号名(如 demo.Demo)请求,返回序列化的 FileDescriptorProto。客户端解析后就能知道方法的输入输出类型、是否是流、字段的名字和编号。
err=<nil>;响应(protojson){"msg":"echo:hello", "n":7}
请求的 Go 类型 *dynamicpb.Message,响应的 Go 类型 *dynamicpb.Message(都是由反射返回的描述符构造的 dynamicpb.Message,不是生成的 pb.Req / pb.Resp)有了描述符就能用 dynamicpb 构造消息,再用 conn.Invoke 按方法全名调用,不需要任何生成的代码。grpcurl 就是这么工作的。
err=<nil>;ErrorResponse code=5 message="proto:\u00a0not found" 同一个流里问 3 次:客户端发出 HEADERS 1 个;开 3 个流各问 1 次:HEADERS 3 个
找不到符号时,RPC 是成功的,错误在响应的 ErrorResponse 里;反射是双向流,一个流里可以连续问多次。