kitex icon indicating copy to clipboard operation
kitex copied to clipboard

[grpc] 无法动态指定 serviceName

Open jxskiss opened this issue 3 years ago • 3 comments

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。

jxskiss avatar Jan 17 '23 06:01 jxskiss

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

jxskiss avatar Jan 17 '23 06:01 jxskiss

多service注册是2023.Q1计划支持的,现在主要问题在于kitex对serviceinfo的使用上是全局唯一的,如果要实现需要对这块进行重构。

jayantxie avatar Jan 18 '23 03:01 jayantxie

你的改法可以解决问题,但实现上比较trick,如果你有急迫的需求可以先fork解决,我们会在这个Q支持完成后,通知你使用多service功能。

jayantxie avatar Jan 18 '23 04:01 jayantxie

抱歉忘了通知你,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。

jayantxie avatar Dec 09 '24 11:12 jayantxie