[grpc] 无法动态指定 serviceName
Describe the bug
当使用 kitex 调用下游原生 grpc 服务时,无法动态指定 serviceName,比如使用 "github.com/jhump/protoreflect/grpcreflect" 这个包调用下游 grpc 服务查询 reflection 信息,通过对 kitex client.Client 简单适配可以传给 grpcreflect.NewClientAuto 这个函数使用,但是 grpcreflect 包里会先尝试调用 "/grpc.reflection.v1.ServerReflection/ServerReflectionInfo",如果返回 Unimplemented 错误,再尝试调用 "/grpc.reflection.v1alpha.ServerReflection/ServerReflectionInfo",因为无法指定调用下游 grpc 的 serviceName, method,在不改动 kitex 代码的情况下这个就无法实现。
func (c *serverReflectionClient) ServerReflectionInfo(ctx context.Context, opts ...grpc.CallOption) (ServerReflection_ServerReflectionInfoClient, error) {
stream, err := c.cc.NewStream(ctx, &ServerReflection_ServiceDesc.Streams[0], "/grpc.reflection.v1.ServerReflection/ServerReflectionInfo", opts...)
if err != nil {
return nil, err
}
x := &serverReflectionServerReflectionInfoClient{stream}
return x, nil
}
希望有一种方式可以在调用方法时指定调用的 serviceName, method。同时如果 kitex grpc server 要支持 reflection 和在一个服务实例注册多个 service 的特性,调用侧可能也需要这样的机制指定 serviceName, method。
A possible solution may be like this, not sure whether it has bad effects, or is there any better solution.
diff --git a/pkg/remote/trans/nphttp2/client_conn.go b/pkg/remote/trans/nphttp2/client_conn.go
index ae87dfd..789dec8 100644
--- a/pkg/remote/trans/nphttp2/client_conn.go
+++ b/pkg/remote/trans/nphttp2/client_conn.go
@@ -55,11 +55,20 @@ func (c *clientConn) ReadFrame() (hdr, data []byte, err error) {
func newClientConn(ctx context.Context, tr grpc.ClientTransport, addr string) (*clientConn, error) {
ri := rpcinfo.GetRPCInfo(ctx)
- var svcName string
- if ri.Invocation().PackageName() == "" {
- svcName = ri.Invocation().ServiceName()
+
+ // grpc method format /package.Service/Method
+ // If the full path is specified (begins with "/") by caller, use it.
+ var fullPath string
+ if methodName := ri.Invocation().MethodName(); len(methodName) > 0 && methodName[0] == '/' {
+ fullPath = methodName
} else {
- svcName = fmt.Sprintf("%s.%s", ri.Invocation().PackageName(), ri.Invocation().ServiceName())
+ var svcName string
+ if ri.Invocation().PackageName() == "" {
+ svcName = ri.Invocation().ServiceName()
+ } else {
+ svcName = fmt.Sprintf("%s.%s", ri.Invocation().PackageName(), ri.Invocation().ServiceName())
+ }
+ fullPath = fmt.Sprintf("/%s/%s", svcName, methodName)
}
host := ri.To().ServiceName()
@@ -72,9 +81,8 @@ func newClientConn(ctx context.Context, tr grpc.ClientTransport, addr string) (*
}
s, err := tr.NewStream(ctx, &grpc.CallHdr{
- Host: host,
- // grpc method format /package.Service/Method
- Method: fmt.Sprintf("/%s/%s", svcName, ri.Invocation().MethodName()),
+ Host: host,
+ Method: fullPath,
})
if err != nil {
return nil, err
多service注册是2023.Q1计划支持的,现在主要问题在于kitex对serviceinfo的使用上是全局唯一的,如果要实现需要对这块进行重构。
你的改法可以解决问题,但实现上比较trick,如果你有急迫的需求可以先fork解决,我们会在这个Q支持完成后,通知你使用多service功能。
抱歉忘了通知你,https://www.cloudwego.io/zh/docs/kitex/tutorials/advanced-feature/multi_service/multi_service/ ,Kitex v0.8.0已经支持 server 注册 multi service了,你可以通过RegisterService函数注册service info,或者直接调用生成代码中的RegisterService函数简化使用。对于client,可使用对应service的client桩代码来访问特定service。