The Golang gRPC practice uses FlatBuffers encoding in gRPC

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

Keywords: Go grpc

Added by webwannabee on Wed, 15 Dec 2021 19:41:12 +0200