introduce
gRPC uses Protocol Buffers by default, and supports other codes, such as JSON and FlatBuffers.
FlatBuffers is a cross platform serialization library designed to achieve maximum memory efficiency. It allows you to directly access serialized data without first parsing / unpacking it, while still having good forward / backward compatibility.
Project address: https://github.com/google/flatbuffers
FlatBuffers are much faster than Protocol Buffers in decoding performance. Here are two articles detailing the comparison between Protocol Buffers and FlatBuffers:
https://blog.csdn.net/chosen0ne/article/details/43033575
https://juzii.gitee.io/2020/03/02/protobuf-vs-flatbuffer/
Here is an article that introduces the writing of FlatBuffers and schema in detail:
https://halfrost.com/flatbuffers_schema/
Here is a demonstration of how to use FlatBuffers in gRPC
code
Write fbs interface definition file
api/fbs/greeter.fbs
namespace models; table HelloReply { message:string; } table HelloRequest { name:string; } table ManyHellosRequest { name:string; num_greetings:int; } rpc_service Greeter { SayHello(HelloRequest):HelloReply; SayManyHellos(ManyHellosRequest):HelloReply (streaming: "server"); }
The definition here is similar to that of protobuf. table is used to define the structure and RCP is used_ Service defines the interface.
Here, three structures are defined for data sending and receiving, and two interfaces are defined for demonstration.
Generate gRPC code
Install flatc first and download the corresponding version of flatc from the following address
https://github.com/google/flatbuffers/releases/tag/v2.0.0
flatc --go --grpc -o api/ api/fbs/greeter.fbs
Parameter Description:
--Go specifies that the generated language is go
--Grpc specifies to generate grpc code
-o optional, specify the directory prefix of the target file to be generated
--Go namespace optional. Specify the generated package name and overwrite the definition in the fbs file
A models directory will be generated in the specified directory, which is the generated code. The directory name is the namespace defined in the fbs file. You can also override this value through the parameter '-- go namespace to specify a new directory, such as:
flatc --go --go-namespace newmodels --grpc -o api/ api/fbs/greeter.fbs
It is recommended to define a namespace through fbs, which is also the package name of the Go file.
The generated file directory is as follows:
├── api │ ├── fbs │ │ └── greeter.fbs │ └── models │ ├── Greeter_grpc.go │ ├── HelloReply.go │ ├── HelloRequest.go │ └── ManyHellosRequest.go
Now we can write gRPC code.
Initialize go mod
Execute in the project root directory:
go mod init github.com/safeie/grpc-flatbuffers-example go mod tidy
Write gRPC server
cmd/server/main.go
package main import ( "context" "fmt" "log" "net" "github.com/safeie/grpc-flatbuffers-example/api/models" "google.golang.org/grpc" flatbuffers "github.com/google/flatbuffers/go" ) var ( greetings = [...]string{"Hi", "Hallo", "Ciao"} ) type greeterServer struct { models.UnimplementedGreeterServer } func (s *greeterServer) SayHello(ctx context.Context, request *models.HelloRequest) (*flatbuffers.Builder, error) { v := request.Name() var m string if v == nil { m = "Unknown" } else { m = string(v) } b := flatbuffers.NewBuilder(0) idx := b.CreateString("Welcome " + m) models.HelloReplyStart(b) models.HelloReplyAddMessage(b, idx) b.Finish(models.HelloReplyEnd(b)) return b, nil } func (s *greeterServer) SayManyHellos(request *models.ManyHellosRequest, stream models.Greeter_SayManyHellosServer) error { v := request.Name() var m string if v == nil { m = "Unknown" } else { m = string(v) } num := request.NumGreetings() b := flatbuffers.NewBuilder(0) for _, greeting := range greetings { idx := b.CreateString(fmt.Sprintf("%s %s ,num %d", greeting, m, num)) models.HelloReplyStart(b) models.HelloReplyAddMessage(b, idx) b.Finish(models.HelloReplyEnd(b)) if err := stream.Send(b); err != nil { return err } } return nil } func newServer() *greeterServer { return &greeterServer{} } func main() { lis, err := net.Listen("tcp", fmt.Sprintf("localhost:%d", 3000)) if err != nil { log.Fatalf("Falied to listen: %v", err) } codec := &flatbuffers.FlatbuffersCodec{} grpcServer := grpc.NewServer(grpc.ForceServerCodec(codec)) models.RegisterGreeterServer(grpcServer, newServer()) if err := grpcServer.Serve(lis); err != nil { fmt.Println(err) panic(err) } }
You can go build test. If there is a dependency problem, go back to the root directory and execute go mod tidy to download the dependency.
Write gRPC client
cmd/client/main.go
package main import ( "context" "flag" "fmt" "io" "log" "math/rand" "time" flatbuffers "github.com/google/flatbuffers/go" "github.com/safeie/grpc-flatbuffers-example/api/models" "google.golang.org/grpc" ) var ( addr = "3000" name = flag.String("name", "Flatbuffers", "name to be sent go server :D") ) func printSayHello(client models.GreeterClient, name string) { log.Printf("Name to be sent (%s)", name) b := flatbuffers.NewBuilder(0) i := b.CreateString(name) models.HelloRequestStart(b) models.HelloRequestAddName(b, i) b.Finish(models.HelloRequestEnd(b)) ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() request, err := client.SayHello(ctx, b, grpc.CallContentSubtype("flatbuffers")) if err != nil { log.Fatalf("%v.SayHello(_) = _, %v: ", client, err) } log.Printf("server said %q", request.Message()) } func printSayManyHello(client models.GreeterClient, name string, num int32) { log.Printf("Name to be sent (%s), num to be sent (%d)", name, num) b := flatbuffers.NewBuilder(0) i := b.CreateString(name) models.ManyHellosRequestStart(b) models.ManyHellosRequestAddName(b, i) models.ManyHellosRequestAddNumGreetings(b, num) b.Finish(models.ManyHellosRequestEnd(b)) ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() stream, err := client.SayManyHellos(ctx, b, grpc.CallContentSubtype("flatbuffers")) if err != nil { log.Fatalf("%v.SayManyHellos(_) = _, %v", client, err) } for { request, err := stream.Recv() if err == io.EOF { break } if err != nil { log.Fatalf("%v.SayManyHellos(_) = _, %v", client, err) } log.Printf("server said %q", request.Message()) } } func main() { flag.Parse() conn, err := grpc.Dial(fmt.Sprintf("localhost:%s", addr), grpc.WithInsecure(), grpc.WithCodec(flatbuffers.FlatbuffersCodec{})) if err != nil { log.Fatalf("Failed to dial: %v", err) } defer conn.Close() client := models.NewGreeterClient(conn) printSayHello(client, *name) num := rand.Int31() printSayManyHello(client, *name, num) }
You can go build test. If there is a dependency problem, go back to the root directory and execute go mod tidy to download the dependency.
Run test
Open a command line window to run: CD CMD / Server & & go run main go
Open a command line window to run: CD CMD / client & & go run main go
The output should look like this:
2021/12/15 18:04:16 Name to be sent (Flatbuffers) 2021/12/15 18:04:16 server said "Welcome Flatbuffers" 2021/12/15 18:04:16 Name to be sent (Flatbuffers), num to be sent (1298498081) 2021/12/15 18:04:16 server said "Hi Flatbuffers ,num 1298498081" 2021/12/15 18:04:16 server said "Hallo Flatbuffers ,num 1298498081" 2021/12/15 18:04:16 server said "Ciao Flatbuffers ,num 1298498081"
Completed sample project code: https://github.com/safeie/grpc-flatbuffers-example
Reference link:
https://github.com/google/flatbuffers/tree/master/grpc/examples/go/greeter