Protocol Buffers and GRPC

Protocol Buffers

Protocol buffers是由Google推出来的用于描述结构化数据的一种数据格式,类似于XML,但更小更快更简单,语言无关,平台无关而且支持可扩展。通过描述性语言定义你所需要的数据结构,可以实现一次定义即可以生成多种语言的相关的代码。目前Protocal buffers基本上已支持大多数的编程语言。

本文将介绍在Go语言中怎么使用Protocol buffers

1.protoc安装
下载所需的版本,解压设置环境变量
2.protoc-gen-go的安装
go install google.golang.org/protobuf/cmd/protoc-gen-go

3.新建一个Go Module

1
2
3
4
5
➜  gopro mkdir grpc
➜ gopro cd grpc
➜ grpc mkdir src
➜ cd src
➜ src go mod init example.com/grpc

整个目录的结构如下

4.在protofiles中编辑person.pb.go文件。如下代码用protobuf声明式语言定义了一个数据结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
syntax = "proto3";

message Person {
string name = 1;
int32 id = 2;
string email = 3;
enum PhoneType {
MOBILE = 0;
HOME = 1;
WORK = 2;
}
message PhoneNumber {
string number = 1;
PhoneType type = 2;
}
repeated PhoneNumber phones = 4;
}

message AddressBook {
repeated Person people = 1;
}

5.通过proto生成Go语言对应的文件
grpc/src/protofiles执行protoc --go_out=. *.proto会在当前目录下生成
文件person.pb.go
6.编写main.gomain_json.go文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// main.go
package main

import (
pb "example.com/grpc/protofiles" //我们可以使用person.pb.go中的结构傅Person
"fmt"
"github.com/golang/protobuf/proto"
)

func main() {
// 对Person进行初始化,并将指针赋值给变量p
p := &pb.Person{
Id: 1234,
Name: "Roger F",
Email: "rf@gmail.com",
Phones: []*pb.Person_PhoneNumber{
{Number: "555-4321", Type: pb.Person_HOME},
},
}

p1 := &pb.Person{}
body, _ := proto.Marshal(p)
_ = proto.Unmarshal(body, p1)
fmt.Println("Original struct loadef from proto file:",p,"\n")
fmt.Println("Marshaled proto data: ", body, "\n")
fmt.Println("Unmarshaled struct: ", p1)
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// main_json.go
package main

import (
"encoding/json"
pb "example.com/grpc/protofiles"
"fmt"

)

func main() {
p := &pb.Person{
Id: 1234,
Name: "Roger F",
Email: "rf@gmail.com",
Phones: []*pb.Person_PhoneNumber{
{Number: "555-4321", Type: pb.Person_HOME},
},
}


body, _ := json.Marshal(p)

fmt.Println(string(body))

}

7.执行go mod tidy安装依赖。
8.分别执行main.gomain_json.go

Go类型与对应的Protobuf类型
Go Protobuf type
float32 float
float64 double
uint32 fixed32
uint64 fixed64
[]byte bytes
Protobut type Default value
string “”
bytes empty bytes[]
bool false
int,int32,int64,float,double 0
enum 0

GRPC

1.安装grpcGo语言相关库
go get google.golang.org/grpc
go get google.golang.org/grpc/cmd/protoc-gen-go-grpc
2.在src目录下新建grpc文件夹,最终的目录结构如下。

3.定义transaction.proto

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
syntax = "proto3";
package grpc;

message TransactionRequest {
string from = 1;
string to = 2;
float amount = 3;
}

message TransactionResponse {
bool confirmation = 1;
}

service MoneyTransaction {
rpc MakeTransaction(TransactionRequest) returns (TransactionResponse) {}
}

4.执行命令protoc --go_out=plugins=grpc:. transaction.proto,会在grpc目录下生成文件transaction.pb.go,文件中的部分内容。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
type TransactionRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields

From string `protobuf:"bytes,1,opt,name=from,proto3" json:"from,omitempty"`
To string `protobuf:"bytes,2,opt,name=to,proto3" json:"to,omitempty"`
Amount float32 `protobuf:"fixed32,3,opt,name=amount,proto3" json:"amount,omitempty"`
}

type TransactionResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields

Confirmation bool `protobuf:"varint,1,opt,name=confirmation,proto3" json:"confirmation,omitempty"`
}

type moneyTransactionClient struct {
cc grpc.ClientConnInterface
}

func NewMoneyTransactionClient(cc grpc.ClientConnInterface) MoneyTransactionClient {
return &moneyTransactionClient{cc}
}

func (c *moneyTransactionClient) MakeTransaction(ctx context.Context, in *TransactionRequest, opts ...grpc.CallOption) (*TransactionResponse, error) {
out := new(TransactionResponse)
err := c.cc.Invoke(ctx, "/grpc.MoneyTransaction/MakeTransaction", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}

func RegisterMoneyTransactionServer(s *grpc.Server, srv MoneyTransactionServer) {
s.RegisterService(&_MoneyTransaction_serviceDesc, srv)
}

5.编写client.goserver.go

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
// server.go
package main

import (
"log"
"net"

pb "example.com/grpc/grpc"
"golang.org/x/net/context"
"google.golang.org/grpc"
"google.golang.org/grpc/reflection"
)

const (
port = ":50051"
)

// server is used to create MoneyTransactionServer.
type server struct{}

// MakeTransaction implements MoneyTransactionServer.MakeTransaction
func (s *server) MakeTransaction(ctx context.Context, in *pb.TransactionRequest) (*pb.TransactionResponse, error) {
log.Printf("Got request for money Transfer....")
log.Printf("Amount: %f, From A/c:%s, To A/c:%s", in.Amount, in.From, in.To)
// Do database logic here....
return &pb.TransactionResponse{Confirmation: true}, nil
}

func main() {
lis, err := net.Listen("tcp", port)
if err != nil {
log.Fatalf("Failed to listen: %v", err)
}
s := grpc.NewServer()
pb.RegisterMoneyTransactionServer(s, &server{})
// Register reflection service on gRPC server.
reflection.Register(s)
if err := s.Serve(lis); err != nil {
log.Fatalf("Failed to serve: %v", err)
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// client.go
package main

import (
"log"

pb "example.com/grpc/grpc"
"golang.org/x/net/context"
"google.golang.org/grpc"
)

const (
address = "localhost:50051"
)

func main() {
// Set up a connection to the server.
conn, err := grpc.Dial(address, grpc.WithInsecure())
if err != nil {
log.Fatalf("Did not connect: %v", err)
}
defer conn.Close()
//创建一个rpc客户端
c := pb.NewMoneyTransactionClient(conn)

// Prepare data. Get this from clients like Frontend or App
from := "1234"
to := "5678"
amount := float32(1250.75)

// Contact the server and print out its response.
r, err := c.MakeTransaction(context.Background(), &pb.TransactionRequest{From: from,
To: to, Amount: amount})
if err != nil {
log.Fatalf("Could not transact: %v", err)
}
log.Printf("Transaction confirmed: %t", r.Confirmation)
}

6.分别执行server.goclient.go


Ref:
1.https://developers.google.com/protocol-buffers/docs/gotutorial
2.Building RESTful Web services with Go
3.GRPC