【笔记】Protobuf学习笔记

前言

GRPC的Protobuf学习笔记

注释

1
// 单行注释
1
/* 多行注释 */

指定Protobuf版本

1
syntax = "proto3";

定义包名

1
package <package_name>;

定义选项

github.com/<author>/<repository>:以当前mod的包名为基准
<repository>/<dir>:Protobuf相对于当前mod根目录的存放路径
<alias_name>:Protobuf的别名

1
option go_package="github.com/<author>/<repository>/<dir>;<alias_name>";

定义结构体

  • 结构体名首字母大写

定义基本类型

1
2
3
4
5
message 结构体名 {
string 字段名 = 1;
int32 字段名 = 2;
bool 字段名 = 3;
}

定义切片

1
2
3
message 结构体名 {
repeated string 字段名 = 1;
}

定义映射

1
2
3
message 结构体名 {
map <string, string> 字段名 = 1;
}

定义结构体嵌套

1
2
3
4
5
6
message 结构体名1 {
message 结构体名2 {
数据类型 字段名 = 1;
}
结构体名2 数据类型 字段名 = 2;
}

定义枚举

1
2
3
4
5
6
7
message 结构体名 {
enum Sex {
male = 0;
female = 1;
}
Sex 字段名 = 1;
}

允许多个值相同的枚举

1
2
3
4
5
6
7
8
9
message 结构体名 {
enum Sex {
option allow_alias = true;
male = 0;
female = 1;
girl = 1;
}
Sex 字段名 = 1;
}

定义OneOf

  • 每次只有一个字段可以被赋值
1
2
3
4
5
6
message 结构体名 {
oneof OneOf名 {
string 字段名1 = 1;
string 字段名2 = 2;
}
}

设置字段为保留字

通过字段名设置字段为保留字

1
reserved "字段名";
保留多个
1
reserved "字段名1", "字段名2";

通过唯一标识设置字段为保留字

1
reserved 1;

引入其他Protobuf

1
import "<dir>/<file>.proto";

调用引入的其他Protobuf

1
2
3
message 结构体名 {
<package_name>.结构体名 字段名 = 1
}

定义服务

  • 服务名首字母大写

即刻响应的服务

1
2
3
service 服务名 {
rpc 函数名(结构体名) returns (结构体名);
}

入参为流的服务

1
2
3
service 服务名 {
rpc 函数名(stream 结构体名) returns (结构体名);
}

出参为流的服务

1
2
3
service 服务名 {
rpc 函数名(结构体名) returns (stream 结构体名);
}

出入参都为流的服务

1
2
3
service 服务名 {
rpc 函数名(stream 结构体名) returns (stream 结构体名);
}

编译Protobuf

1
protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative <dir>/<file>.proto

调用服务

即刻响应的服务

服务端

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 (
"context"
"github.com/feiju12138/grpc/test"
"google.golang.org/grpc"
"net"
)

type TestService struct {
test.UnimplementedTestServiceServer
}

func (*TestService) Test(context context.Context, req *test.Req) (*test.Res, error) {
return &test.Res{Message: req.GetMessage()}, nil
}

func main() {
listen, _ := net.Listen("tcp", "0.0.0.0:8080")
server := grpc.NewServer()
test.RegisterTestServiceServer(server, &TestService{})
server.Serve(listen)
}

客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package main

import (
"context"
"fmt"
"github.com/feiju12138/grpc/test"
"google.golang.org/grpc"
)

func main() {
listen, _ := grpc.Dial("127.0.0.1:8080", grpc.WithInsecure())
client := test.NewTestServiceClient(listen)
res, _ := client.Test(context.Background(), &test.Req{Message: ""})
fmt.Println(res)
}

入参为流的服务

服务端

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

import (
"context"
"fmt"
"github.com/feiju12138/grpc/test"
"google.golang.org/grpc"
"net"
)

type TestService struct {
test.UnimplementedTestServiceServer
}

func (*TestService) TestIn(server test.TestService_TestInServer) error {
for {
req, err := server.Recv()
if err != nil {
server.SendAndClose(&test.Res{Message: "EOF"})
break
}
fmt.Println(req)
}
return nil
}

func main() {
listen, _ := net.Listen("tcp", "0.0.0.0:8080")
server := grpc.NewServer()
test.RegisterTestServiceServer(server, &TestService{})
server.Serve(listen)
}

客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package main

import (
"context"
"fmt"
"github.com/feiju12138/grpc/test"
"google.golang.org/grpc"
)

func main() {
listen, _ := grpc.Dial("127.0.0.1:8080", grpc.WithInsecure())
client := test.NewTestServiceClient(listen)
c, _ := client.TestIn(context.Background())
c.Send(&test.Req{Message: ""})
c.Send(&test.Req{Message: ""})
c.Send(&test.Req{Message: ""})
res, _ := c.CloseAndRecv()
fmt.Println(res)
}

出参为流的服务

服务端

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

import (
"context"
"fmt"
"github.com/feiju12138/grpc/test"
"google.golang.org/grpc"
"net"
)

type TestService struct {
test.UnimplementedTestServiceServer
}

func (*TestService) TestOut(req *test.Req, server test.TestService_TestOutServer) error {
server.Send(&test.Res{Message: req.GetMessage()})
server.Send(&test.Res{Message: req.GetMessage()})
server.Send(&test.Res{Message: req.GetMessage()})
return nil
}

func main() {
listen, _ := net.Listen("tcp", "0.0.0.0:8080")
server := grpc.NewServer()
test.RegisterTestServiceServer(server, &TestService{})
server.Serve(listen)
}

客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package main

import (
"context"
"fmt"
"github.com/feiju12138/grpc/test"
"google.golang.org/grpc"
)

func main() {
listen, _ := grpc.Dial("127.0.0.1:8080", grpc.WithInsecure())
client := test.NewTestServiceClient(listen)
c, _ := client.TestOut(context.Background(), &test.Req{Message: ""})
for {
res, err := c.Recv()
fmt.Println(res)
if err != nil {
break
}
}
}

出入参都为流的服务

服务端

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

import (
"context"
"fmt"
"github.com/feiju12138/grpc/test"
"google.golang.org/grpc"
"net"
)

type TestService struct {
test.UnimplementedTestServiceServer
}

func (*TestService) TestIO(server test.TestService_TestIOServer) error {
channel := make(chan string)
go func() {
for {
res, err := server.Recv()
if err != nil {
channel <- "EOF"
}
channel <- res.GetMessage()
}
}()
for {
c := <-channel
if c == "EOF" {
break
}
server.Send(&test.Res{Message: ""})
}
return nil
}

func main() {
listen, _ := net.Listen("tcp", "0.0.0.0:8080")
server := grpc.NewServer()
test.RegisterTestServiceServer(server, &TestService{})
server.Serve(listen)
}

客户端

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

import (
"context"
"fmt"
"github.com/feiju12138/grpc/test"
"google.golang.org/grpc"
"sync"
)

func main() {
listen, _ := grpc.Dial("127.0.0.1:8080", grpc.WithInsecure())
client := test.NewTestServiceClient(listen)
c, _ := client.TestIO(context.Background())
var waitGroup = sync.WaitGroup{}
waitGroup.Add(2)
go func() {
for {
err := c.Send(&test.Req{Message: ""})
if err != nil {
waitGroup.Done()
break
}
}
}()
go func() {
for {
res, err := c.Recv()
if err != nil {
waitGroup.Done()
break
}
fmt.Println(res)
}
}()
waitGroup.Wait()
}

完成

参考文献

哔哩哔哩——go圈里最会写js的奇淼