Reputation: 825
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
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