protoreflect icon indicating copy to clipboard operation
protoreflect copied to clipboard

Could we add some examples for each module?

Open umialpha opened this issue 5 years ago • 8 comments

Hi, I am glad to find this powerful pkg. However, their is no tutorial or example to show how to use it. Could we add some tutorials or examples for each module?

umialpha avatar Jul 02 '20 07:07 umialpha

BTW, my usecase is as follows, we deploy client's gRPC servers in our cluster. client will send request to our proxy saying" I want ADD 1, 2 ", our proxy will parse the request and act like the real gRPC client, sending request to multiple gRPC server, return the fastest response to client.

ClientA ----> Proxy -- --> Server A & B

umialpha avatar Jul 02 '20 07:07 umialpha

+1 for examples

Paxa avatar Jul 12 '20 07:07 Paxa

I can suggest some example:

  • Parse .proto file
  • Create dynamic message
  • Send message to GRPC
  • marshal and unmarshal binary data

package main

import (
	"context"
	"fmt"
	"log"
	"os"

	"github.com/jhump/protoreflect/desc"
	"github.com/jhump/protoreflect/desc/protoparse"
	"github.com/jhump/protoreflect/dynamic"
	"github.com/jhump/protoreflect/dynamic/grpcdynamic"
	"google.golang.org/grpc"
)

const GRPC_SERVER = "127.0.0.1:10000"

// proto file https://github.com/grpc/grpc-go/blob/master/examples/route_guide/routeguide/route_guide.proto

func loadProto() *desc.FileDescriptor {
	parser := protoparse.Parser{}
	parser.ImportPaths = []string{
		os.Getenv("HOME") + "/go/src/google.golang.org/grpc/examples/route_guide/routeguide",
	}

	descs, err := parser.ParseFiles("route_guide.proto")

	if err != nil {
		panic(err)
	}

	log.Printf("Loaded descriptor content:")

	for _, desc := range descs {
		for _, service := range desc.GetServices() {
			log.Printf("> service %s", service.GetFullyQualifiedName())
			for _, method := range service.GetMethods() {
				log.Printf("  * method %s (%s) %s",
					method.GetFullyQualifiedName(),
					method.GetInputType().GetFullyQualifiedName(),
					method.GetOutputType().GetFullyQualifiedName(),
				)
			}
		}
		for _, msg := range desc.GetMessageTypes() {
			log.Printf("- message %s", msg.GetFullyQualifiedName())
		}
	}

	return descs[0]
}

// Dynamically create message using parsed proto descriptor
func composeMessage(descriptor *desc.FileDescriptor) *dynamic.Message {
	messageDesc := descriptor.FindMessage("routeguide.Point")
	if messageDesc == nil {
		panic(fmt.Errorf("Can't find message"))
	}
	message := dynamic.NewMessage(messageDesc)

	/*
		message Point {
			int32 latitude = 1;
			int32 longitude = 2;
		}
	*/

	message.SetFieldByName("latitude", int32(12345678))
	message.SetFieldByName("longitude", int32(87654321))

	return message
}

// Require GRPC server running
//    cd ~/go/src/google.golang.org/grpc/examples/route_guide
//    go run server/server.go
//
func callGrpc(descriptor *desc.FileDescriptor) {
	serviceName := "routeguide.RouteGuide"
	methodName := "GetFeature"

	log.Printf("Looking for serviceName %s methodName %s", serviceName, methodName)

	// FIND DYNAMIC DESCRIPTORS FOR SERVICE AND METHOD

	serviceDesc := descriptor.FindService(serviceName)
	if serviceDesc == nil {
		panic(fmt.Errorf("Can't find service %s", serviceName))
	}

	methodDesc := serviceDesc.FindMethodByName(methodName)
	if methodDesc == nil {
		panic(fmt.Errorf("Can't find method %s in service %s", methodName, serviceName))
	}

	// CREATE GRPC CLIENT

	log.Printf("Creating GRPC client for %s", GRPC_SERVER)
	conn, err := grpc.Dial(GRPC_SERVER, grpc.WithInsecure())
	if err != nil {
		panic(err)
	}
	defer conn.Close()

	grpcClient := grpcdynamic.NewStub(conn)

	// SENDING MESSAGE

	message := composeMessage(descriptor)
	response, err := grpcClient.InvokeRpc(context.TODO(), methodDesc, message)

	if err != nil {
		panic(err)
	}

	log.Printf("response %#v", response)

	json, err := response.(*dynamic.Message).MarshalJSON()
	log.Printf("response json %s %v", json, err)
}

// Convert dynamic.Message to binary string (proto encoded) and parse proto string to dynamic.Message
func toAndFromBin(descriptor *desc.FileDescriptor) {
	message := composeMessage(descriptor)

	data, err := message.Marshal()
	log.Printf("Binary data %v  %v", data, err)

	parsedDessage := dynamic.NewMessage(descriptor.FindMessage("routeguide.Point"))
	err = parsedDessage.Unmarshal(data)

	log.Printf("Parsed from binary %v  %v", message, err)
}

func main() {
	descriptor := loadProto()

	fmt.Println("")
	log.Printf("Creating message dynamicly of type routeguide.Point")
	fmt.Println("")

	point := composeMessage(descriptor)
	json, err := point.MarshalJSON()
	log.Printf("message routeguide.Point as json: %s %v", json, err)

	fmt.Println("")
	log.Printf("Calling grpc endpoint routeguide.Point")
	fmt.Println("")

	callGrpc(descriptor)

	fmt.Println("")
	log.Printf("Work with binary strings")
	fmt.Println("")

	toAndFromBin(descriptor)
}

Paxa avatar Jul 12 '20 11:07 Paxa

@Paxa Thanks a lot. Your example explains pretty well.

umialpha avatar Jul 16 '20 07:07 umialpha

@Paxa How do I read the incoming metadata after invokeRPC call i.e., response, err := grpcClient.InvokeRpc(context.TODO(), methodDesc, message)

LSTPro avatar Apr 04 '24 14:04 LSTPro

@LSTPro, that's really a grpc-go question, not specific to this repo. https://github.com/grpc/grpc-go/blob/master/Documentation/grpc-metadata.md#receiving-metadata

jhump avatar Apr 04 '24 15:04 jhump

@jhump The link provided was helpful, Thank you!

LSTPro avatar Apr 04 '24 21:04 LSTPro