rameshmani
rameshmani

Reputation: 825

gRPC GO Single Generic Service Handler

I have used protoreflect to write http-to-gRPC-gateway. But I don't see any easy option to do the reverse - gRPC-to-HTTP-gateway. One of the idea is to expose GRPC to outside but use a custom gateway to invoke internal microservices via MessageQ to avoid loadbalancing, discovery service and may be convert GRPC streaming durable or use reflection to invoke method instead of using generated server stub

Below is what I got so far

func main() {
    lis, err := net.Listen("tcp", ":9091")
    grpcServer := grpc.NewServer(grpc.UnknownServiceHandler(GenericHandler))
    //pb.RegisterGreeterServer(grpcServer, &TestService{})
    grpcServer.Serve(lis)
}

func GenericHandler(srv interface{}, stream grpc.ServerStream) error {
    fullMethodName, ok := grpc.MethodFromServerStream(stream)
    log.Printf("Method: %v, %v\n", fullMethodName, ok)
    //how to get protobuf payload from stream
    //Response:=Invoke Method via Reflection or http or MessageQueue - passing either the raw protocal buffer or as json
    //how to return Response
    return nil
}

The only way I see to achieve this is understand and re-implement actual handler processUnaryRPC

Upvotes: 5

Views: 1409

Answers (1)

rameshmani
rameshmani

Reputation: 825

Got it to work, thanks to protoreflect

Working sample without error handling

//Parse protofile, create grpc.ServiceDesc, register
func (s *GRPCService) LoadSpec(protoFileName string) {
    p := protoparse.Parser{}
    fdlist, _ := p.ParseFiles(protoFileName)
    for _, fd := range fdlist {
        for _, rsd := range fd.GetServices() {
            s.sdMap[rsd.GetName()] = rsd
            gsd := grpc.ServiceDesc{ServiceName: rsd.GetName(), HandlerType: (*interface{})(nil)}
            for _, m := range rsd.GetMethods() {
                gsd.Methods = append(gsd.Methods, grpc.MethodDesc{MethodName: m.GetName(), Handler: s.Handler})
            }
            s.grpcServer.RegisterService(&gsd, s)
        }
    }
}

func (s *GRPCService) Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
    stream := grpc.ServerTransportStreamFromContext(ctx)
    arr := strings.Split(stream.Method(), "/")
    serviceName := arr[1]
    methodName := arr[2]
    service := s.sdMap[serviceName]
    method := service.FindMethodByName(methodName)
    input := dynamic.NewMessage(method.GetInputType())

    dec(input)
    jsonInput, err := input.MarshalJSON()
    log.Printf("Input:%s Err:%v \n", jsonInput, err)
    //jsonOutput:=invokeServiceViaReflectionOrHttp(jsonInput)
    jsonOutput := `{"message":"response"}`

    output := dynamic.NewMessage(method.GetOutputType())
    output.UnmarshalJSON([]byte(jsonOutput))
    return output, nil
}

Upvotes: 3

Related Questions