0%

protobuf进阶

protobuf基本语法

https://developers.google.com/protocol-buffers/docs/proto3#enum

python沟通protobuf时会遇到的问题

若想传一个列表类型,在poroto文件中应使用repeated关键字

1
2
3
4
message HelloRequest {
string name = 1;
repeated int32 id = 2;
}

此时在python代码中不能对其直接实例化后直接赋值,而是应使用列表的方法(如:extend,append)等去改变它的值

此外,嵌套定义message也不能实例化后直接赋值

go_package详解

设置:

1
option go_package = "common/stream/proto/v1";

执行:

cd 到目标文件夹

1
protoc --go_out=plugins=grpc:./ ./hello.proto

即使目标文件夹下不存在common/stream/proto/v1也会生成common/stream/proto/v1/hello.pb.go脚本

或者:

设置:

1
 option go_package = "../common/stream/proto/v1";

执行命令后会在父路径下生成一系列文件夹及文件

protobuf的一点原理

protobuf不是根据名称来对应的,而是根据编号:

1
2
3
4
5
message HelloRequest {
// 根据编号1来传值而不是name
string name = 1;
repeated int32 id = 2;
}

假设对方proto文件为:

1
2
3
4
5
message HelloRequest {
// 根据编号1来传值而不是name
string name = 2;
repeated int32 id = 1;
}

此时会将name映射到id,id映射到name

一个proto文件中引入其它proto文件

应用场景:有一些常用的service与message可能很多proto文件都要用到,这时不妨将这些service与message放到一个公用的proto文件中,在需要调用时import。

1
2
3
4
5
6
7
8
9
10
// base.proto
syntax = "proto3";

message Empty{
// 置空
}

message Pong{
string id = 1;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 文件二
syntax = "proto3";
import "python_grpc_helloworld/proto/base.proto";

service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply) {}
// 一个服务必须有参数,此时我们可以传空参数
rpc Ping (Empty) returns (Pong) {}
}

message HelloRequest {
string name = 1;
}

message HelloReply {
string message = 1;
}

message对象嵌套

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

service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply) {}
}

message HelloRequest {
string name = 1;
}

message HelloReply {
string message = 1;

message Result {
string name = 1;
string age = 2;
}
repeated Result persons = 2;
}

python下使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import grpc
from concurrent import futures
from proto import hello_pb2,hello_pb2_grpc

class Greeter(hello_pb2_grpc.GreeterServicer):

def SayHello(self, request, context):
# 使用嵌套对象时的生成
names = ["name1", "name2", "name3"]
results = []
for name in names:
result = hello_pb2.HelloReply.Result(name=name,age="17")
results.append(result)
return hello_pb2.HelloReply(message=f"hello,{request.name}",persons=results)

if __name__ == "__main__":
server = grpc.server(futures.ThreadPoolExecutor(5))
hello_pb2_grpc.add_GreeterServicer_to_server(servicer=Greeter(),server=server)
server.add_insecure_port('127.0.0.1:50000')
server.start()
server.wait_for_termination()
1
2
3
4
5
6
7
8
import grpc
from python_grpc_helloworld.proto import hello_pb2_grpc,hello_pb2

with grpc.insecure_channel('127.0.0.1:50000') as channel:
stub = hello_pb2_grpc.GreeterStub(channel)
rsp: hello_pb2.HelloReply = stub.SayHello(hello_pb2.HelloRequest(name="lily"))

print(rsp.persons) # l

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
package main

import (
"context"
"net"

"google.golang.org/grpc"

"awesomeProject/grpc_test/proto"
)

type Server struct{}

func (s *Server) SayHello(ctx context.Context, request *proto.HelloRequest) (*proto.HelloReply,error){
var results []*proto.HelloReply_Result
for i:=0;i<10;i++{
result := proto.HelloReply_Result{
Name: "name",
Age: string(i),
}
results = append(results,&result)
}
return &proto.HelloReply{
Message: "Hello" + request.Name,
Persons: results,
},nil
}

func main(){
server := grpc.NewServer()
proto.RegisterGreeterServer(server,&Server{})
listen,err := net.Listen("tcp","127.0.0.1:50000")
if err != nil{
panic("fail to listen:" + err.Error())
}
err = server.Serve(listen)
if err != nil{
panic("fail to start:" + err.Error())
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package main

import (
"awesomeProject/grpc_test/proto"
"context"
"fmt"
"google.golang.org/grpc"
)

func main(){
conn, err := grpc.Dial("127.0.0.1:50000", grpc.WithInsecure())
if err != nil{
panic(err)
}
defer conn.Close()

c := proto.NewGreeterClient(conn)
resp, err := c.SayHello(context.Background(), &proto.HelloRequest{Name:"Lily"})
if err != nil{
panic(err)
}
fmt.Println(resp.Persons)
}

几种类型

枚举

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
syntax = "proto3";

option go_package = "./;proto";

service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply) {}
}


message HelloRequest {
string name = 1;
}

message HelloReply {
string message = 1;

message Result {
string name = 1;
string age = 2;
}
repeated Result persons = 2;

enum Gender{
MALE = 0;
FEMALE = 1;
}

Gender g = 3;
}

生成的相关代码

1
2
3
4
5
6
type HelloReply_Gender int32

const (
HelloReply_MALE HelloReply_Gender = 0
HelloReply_FEMALE HelloReply_Gender = 1
)

map

不建议使用,message本身很像map

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
syntax = "proto3";

option go_package = "./;proto";

service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply) {}
}


message HelloRequest {
string name = 1;
}

message HelloReply {
string message = 1;

message Result {
string name = 1;
string age = 2;
}
repeated Result persons = 2;

enum Gender{
MALE = 0;
FEMALE = 1;
}
Gender g = 3;

map<string ,string >mp = 4;
}
1
2
3
4
5
6
7
// 客户端用法
return &proto.HelloReply{
Message: "Hello" + request.Name,
Persons: results,
G: proto.HelloReply_FEMALE,
Mp: map[string]string{"hhh":"2333","QAQ":"QWQ"},
},nil