protobuf

protobuf 编译器

二进制安装

1.下载二进制包
wget https://github.com/protocolbuffers/protobuf/releases/download/v3.17.3/protoc-3.17.3-linux-x86_64.zip
图片1.png
2.解压设置环境变量

unzip protoc-3.17.3-linux-x86_64.zip -d protoc
// shell 环境变量
vim /etc/profile
    export PROTOC=~/protoc
    export PATH=$PATH:$PROTOC/bin
source /etc/profile
//fish 环境变量
vim ~/.config/fish/config.fish
    set -x PROTOC /home/wzl/protoc
    set -x PATH $PROTOC/bin $PATH
    sudo vim /etc/fish/config.fish
     source  /etc/fish/config.fish

3.测试命令提示
图片2.png

安装protobuf-go

git clone https://github.com/golang/protobuf.git
mv protobuf $GOMODCACHE/github.com/golang/
cd $GOMODCACHE/github.com/golang/protobuf/protoc-gen-go
go build
sudo cp protoc-gen-go /bin
//有自动提示安装成功

图片1.png

数据类型与GO对比

  • .proto | GO
    double | float64
    float | float32
    int32 | int32
    int64 | int64
    uint32 | uint32
    uint64 | uint64
    sint32 | int32
    sint64 | int64
    fixed64 | uint32
    fixed64 | uint64
    bool | bool
    string | string
    bytes | []byte

消息体

1.定义消息体

//默认是proto2
syntax="proto3";
//指定所在包名
package pb;
//编译生成文件路径及包名
option go_package = "./;proto";
//定义消息体
message Person {
    string name = 1;//字段编号,可不从1开始,但不能重复
    int32 age = 2;
}

2.消息体嵌套

syntax="proto3";
package pb;
message Person {
    string name = 1;
    int32 age = 2;
    PhoneNumber p = 3; //消息体可嵌套
}
 message PhoneNumber {
       string number = 1;
       int64 type = 2;
 }

3.字段编号
1)每个字段唯一编号
2)范围:1 - 536870911,其中19000~19999是协议缓冲区保留数

数组

repeated
repeadted关键字类似与go中的切片,编译之后对应的也是go的切片:

syntax="proto3";
package pb;
message Person {
    string name = 1;
    int32 age = 2;
    message PhoneNumber {
       string number = 1;
       int64 type = 2;
    }
    repeated PhoneNumber phone = 3; //数组
}

枚举

enum

syntax="proto3";
package pb;
//定义枚举类型
enum Week {
    Monday = 0; //枚举值,必须从0开始,与写入编号无关
    Turesday =1;
}
message Person {
    string name = 1;
    int32 age = 2;
   //枚举
   Week w = 5;
}

联合体

oneof

syntax="proto3";
package pb;
 
message Person {
    string name = 1;
    int32 age = 2;
    //联合体
    oneof data {
         string teacher = 3; //写入编号不能重复
         string class = 4;
    }
}

map

syntax="proto3";

import "base.proto";
import "google/protobuf/empty.proto";

option go_package = "../pb";

service Greeter {
    rpc SayHello(HelloRequest) returns (HelloReply);
    rpc Ping (google.protobuf.Empty) returns (Pong);
}
message HelloRequest {
    string url = 1;
    string name = 2;
    map<string,string> mp = 4; //map
}
message HelloReply {
    string message = 1;
}

timestamp

syntax = "proto3";
import "google/protobuf/timestamp.proto";
option go_package = "./;proto";
service Greeter {
    rpc SayHello (HelloRequest) returns (HelloReply);
}

message HelloRequest {
    google.protobuf.Timestamp addTime = 5;
}

message HelloReply {
    string message = 1;
}

timestamp.proto文件路径

protoc安装路径/include/google/protobuf/timestamp.proto

生成go package路径

vim timestamp.proto
option go_package = "google.golang.org/protobuf/types/known/timestamppb";
$GOMODCACHE/google.golang.org/protobuf@v1.27.1/types/known/timestamppb/timestamp.pb.go

package main

import (
	"context"
	"fmt"
	"grpcStream/timestamp/proto" // 根据proto文件自动生成的代码
	"log"
	"time"

	"google.golang.org/grpc"
	timestamppb "google.golang.org/protobuf/types/known/timestamppb"
)

func main() {
	// 创建连接
	conn, err := grpc.Dial("localhost:50053", grpc.WithInsecure())
	if err != nil {
		log.Printf("连接失败: [%v]\n", err)
		return
	}
	defer conn.Close()
	// 声明客户端
	client := proto.NewGreeterClient(conn)
	rsp, _ := client.SayHello(context.Background(), &proto.HelloRequest{
		AddTime: timestamppb.New(time.Now()),
	})
	fmt.Println(rsp.Message)
}

metadata

MD类型实际是map,key是string,value是string类型的slice
type MD map[string][]string

1. 创建

//第一种方式
md := metadata.New(map[string]string{"k1":"v1","k2":"v2"})
//第二种方式
md := metadata.Pairs(
"k1","v1",
"k1","v1-2", // k1:[]sring{"v1","v1-2"}
"k2","v2"
)

2. 发送

md := metadata.Pairs("key","val")
ctx := metadata.NewOutgoingContext(context.Background(),md)
response,err := client.SomeRPC(ctx,someRequest)

3. 接收

func (s *server) SomeRPC(ctx context.Context.Context,in pb.SomeRequest) (pb.SomeResponse,error){
md,ok := metadata.FromIncomingContext(ctx)
}

编译protobuf文件

go编译命令
protoc --go_out=./ *.proto ---> xxx.pb.go
c++编译命令
protoc --cpp_out=./ *.proto ---> xxx.pb.cc,xxx.pb.h

编译错误

protoc-gen-go: unable to determine Go import path for "test.proto"
Please specify either:
• a "go_package" option in the .proto source file, or
• a "M" argument on the command line.

修改错误
option go_package = "path/packageName"; // 或 "../pb;pb" 路径;包名

syntax="proto3";
package pb; 
option go_package = "./pb"; //生成包路径
message Person {
    string name = 1;
    int32 age = 2;
    PhoneNumber p = 3; //消息体可嵌套
}
 message PhoneNumber {
       string number = 1;
       int64 type = 2;
 }

protoc命令

protoc -h
-I=path | --proto_path=PATH   path:proto文件目录
--go_out=[paths=source_relative,plugins=grpc:]OUT_DIR  
  paths参数:
      使用 source_relative(相对-I指定的路径) 则不会使用option go_package中指定的路径
      使用 import 则是使用option go_package 中指定的路径
 参数分开: --go_out=. --go_opt=paths=source_relative,plugins=grpc

添加rpc服务

- 语法

service 服务名 {
        rpc    函数名(参数:消息体) returns (返回值:消息体)
}
例
message People {
        string name = 1;
}
message Man {
        int32 age = 2;
}
service hello {
        rpc HelloPeple(People) returns (Man);
}
  • 编译期间,protobuf默认不编译服务,要想使之编译。需要使用gRPC.
  • 编译指令

    protoc --go_out=plugins=grpc:./ *.proto

  • import proto文件
base.proto

syntax = "proto3";
option go_package = ".;proto"
message Empty1{
}
#实例化Pong,需要编译生成base.pb.go文件
message Pong {
    string id = 1;
}
test.proto

syntax="proto3";

option go_package = ".;proto";


import "base.proto";
import "google/protobuf/empty.proto";

service Greeter {
    rpc SayHello(HelloRequest) returns (HelloReply);
    rpc Ping (google.protobuf.Empty) returns (Pong);
}
message HelloRequest {
    string url = 1;
    string name = 2;
}
message HelloReply {
    string message = 1;
}

google/protobuf/empty.proto

序列化

package main

import (
	"ImTransfer/pb"
	"fmt"

	"google.golang.org/protobuf/proto"
)

func main() {
	req := pb.PhoneNumber{
		Number: "123",
		Type:   11,
	}
	rsp, _ := proto.Marshal(&req)
	fmt.Println(string(rsp))
}

图片3.png

反序列化

import (
	"ImTransfer/pb"
	"fmt"

	"google.golang.org/protobuf/proto"
)

func main() {
	req := pb.PhoneNumber{
		Number: "123",
		Type:   11,
	}
	rsp, _ := proto.Marshal(&req)
	fmt.Println(string(rsp))
	//反序列化
	newReq := new(pb.PhoneNumber)
	_ = proto.Unmarshal(rsp, newReq)
	fmt.Println(newReq)
}

图片4.png

posted @ 2021-09-10 21:38  wangzhilei  阅读(473)  评论(0编辑  收藏  举报