introduce
gRPC uses Protocol Buffers encoding by default, and also supports other encodings such as JSON, FlatBuffers, etc.
FlatBuffers is a cross-platform serialization library designed to achieve maximum memory efficiency. It allows you to directly access serialized data without having to parse/unpack it first, while still having good forward/backward compatibility.
Project address: https://github.com/google/flatbuffers
FlatBuffers is 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 details the writing of FlatBuffers and schema:
https://halfrost.com/flatbuffers_schema/
Here is mainly to demonstrate how to use FlatBuffers in gRPC.
coding
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 protobuf, using table
define the structure and rcp_service
define the interface.
Three structures are defined here for data sending and receiving, and two interfaces are defined for demonstration.
Generate gRPC code
First install flatc
and download the corresponding version of flatc from the address below.
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 is optional, specify the generated package name, overwrite the definition in the fbs file
models
directory will be generated in the specified directory, which is the generated code. The directory name is fbs
defined in the namespace
file. You can also '--go-namespace
to specify a new directory, such as:
flatc --go --go-namespace newmodels --grpc -o api/ api/fbs/greeter.fbs
Recommended by fbs
defined namespace
, this namespace
is Go file package
name.
The generated file directory looks like this:
├── 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
. If there is a dependency problem, go back to the root directory and execute go mod tidy
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
. If there is a dependency problem, go back to the root directory and execute go mod tidy
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 example project code: https://github.com/safeie/grpc-flatbuffers-example
Reference link:
https://github.com/google/flatbuffers/tree/master/grpc/examples/go/greeter
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。