Protoful buffer

proto 文件

syntax = "proto3";
// 字段的 tag
// 1到15 只占用一个字节,所以应该用在不频繁使用的字段上。

import "data.proto" //引用其他的包

package my.project; // c# namespace My.Project

option csharp_namespace = "My.WebApis"; // C# 的话生成的命名空间就是 =My.WebApis,其他的还是 my.project



message Preson {
  int32 id = 1;
  string name = 2;
  float height = 3;
  float weight = 4;
  bytes avatar = 5;
  string email = 6;
  bool email_verified = 7;
  repeated string phone_numbers =
      8; //数组的话,使用paked,(一个人可以有多个电话号码)

  Gender gender = 11;
  Date birthday = 12;

  repeated Address addresses = 13; //方式可能是个复数

  reserved 9, 10, 20 to 100; //保留的tag,不能再用了
  reserved "foo", "bar";     //保留的字段

  enum Gender {
    option allow_alias = true; //起好别名了
    NOT_SPECIFIED = 0;         //未指定
    FEMALE = 1;                //男
    MALE = 2;                  //女

    WOMAN = 1; //男
    MAN = 2;   //女
  }

  message Address {
    string province = 1;
    string city = 2;
    string zip_code = 3;
    string street = 4;
    string number = 5;
  }
}

引用的 data 包

syntax = "proto3";
message Date {
  int32 year = 1;
  int32 month = 2;
  int32 day = 3;
}

生成文件

生成csharp文件的命令

protoc first.proto --csharp_out=csharp

下载 go 的依赖

go get -u github.com/golang/protobuf/protoc-gen-go

简单例子

syntax = "proto3";
// package example.first;
package src;
option go_package = "./src"; //输出目录
message PresonMessage {
  int32 id = 1;
  bool is_adult = 2;
  string name = 3;
  repeated int32 lucky_number = 4;
}

生成go的 proto 命令

protoc --go_out=.  person.proto   // 执行命令

序列和反序列化pb

package main

import (
	"fmt"
	"io/ioutil"
	"log"

	"demo01/firstpb"

	"github.com/golang/protobuf/jsonpb"
	"github.com/golang/protobuf/proto"
)

func main() {
	pm := NewPersonMessage()
	// _ = writeToFile("person.bin", pm)
	// pm2 := &firstpb.PresonMessage{}
	// readFromFile("./person.bin", pm2)
	// fmt.Println(pm2)
	pmstr := readToJson(pm)
	fmt.Println(pmstr)

	pm3 := &firstpb.PresonMessage{}
	_ = readfromJson(pmstr, pm3)
	fmt.Println(pm3)
}

// 从 json中读出来,写到 pb 中
func readfromJson(in string, pb proto.Message) error {
	err := jsonpb.UnmarshalString(in, pb)
	if err != nil {
		log.Fatalln("读 json的时候发生错误")
	}
	return nil
}

// 将 pb 结构体中的对象,读出来转成 json 格式
func readToJson(pb *firstpb.PresonMessage) string {
	marshaller := jsonpb.Marshaler{Indent: "    "} //里面是 key 和 value,不要让 json打成一行
	str, err := marshaller.MarshalToString(pb)
	if err != nil {
		log.Fatalln("转化成JSON时候发生错误")
	}
	return str

}

// 从文件中读出来,再反序列化到结构体中
func readFromFile(fileName string, pb proto.Message) error {
	dataBytes, err := ioutil.ReadFile(fileName)
	if err != nil {
		log.Fatalln("读取文件有错误", fileName)
	}
	if err = proto.Unmarshal(dataBytes, pb); err != nil {
		log.Fatalln("无法完成反序列化", err)
	}
	return nil
}

// 将 pb 写到文件中
func writeToFile(fileName string, pb proto.Message) error {
	dataBytes, err := proto.Marshal(pb) //序列化成二进制文件
	if err != nil {
		log.Fatalln("无法序列化")
	}
	if err = ioutil.WriteFile(fileName, dataBytes, 0644); err != nil {
		log.Fatalln("无法写入到文件")
	}
	log.Println("写入文件成功")
	return nil
}

// 新建一个 protobuf 对象的指针,对go来说就是一个结构体
func NewPersonMessage() *firstpb.PresonMessage {
	pm := firstpb.PresonMessage{
		Id:          1234,
		IsAdult:     true,
		Name:        "Dave",
		LuckyNumber: []int32{1, 2, 3, 4, 5},
	}
	pm.Name = "ZZZ"           //可以改名
	fmt.Println(pm.GetId())   //直接使用getid
	fmt.Println(pm.GetName()) //获得名字,尽量不要使用 pm.Name
	return &pm
}

较为复杂的例子

proto文件

syntax = "proto3";
// package example.first;
package exampl.third;          //包名
option go_package = "./third"; //输出目录
message DepartmentMessage {
  int32 id = 1;
  string name = 2;
  repeated EmployeeMessage employee = 3;   //一个部门有多个员工
  DepartmentMessage parent_department = 4; //他的上级部门
  DepartmentMessage child_department = 5;  //他的下级部门
}
message EmployeeMessage { //员工
  int32 id = 1;
  string name = 2;
}

生成proto.go 的文件

创建一个 protobuf对象

package main

import (
	"demo01/third"
	"fmt"
)

func main() {
	dm := NewDepartmentMessage()
	fmt.Println(dm)
}

func NewDepartmentMessage() *third.DepartmentMessage {
	dm := &third.DepartmentMessage{
		Id:   5672,
		Name: "开发部门",
		Employee: []*third.EmployeeMessage{
			{
				Id:   5672,
				Name: "Dave",
			},
			{
				Id:   5688,
				Name: "Mike",
			}},
		ParentDepartment: &third.DepartmentMessage{Id: 110, Name: "总公司"},
		ChildDepartment:  &third.DepartmentMessage{},
	}
	return dm
}

gRPC的结构

对于 gRPC来讲,传输协议只管把消息传过去,但是具体里面的内容是啥,传输协议他是不知道的,需要用绿色部分生成的代码来解析。

设计步骤

gRPC的声明周期

  1. 请求只能是 client 来发送,不能由 server 发起。

  2. 但是 server 发送的可以直接发送。

  3. gGRPC是自带身份认证的。其中包括 4种身份认证机制

    1. 不采取任何认证的连接
    2. TLS/SSL 连接
    3. 基于 Google Token 的身份认证
    4. 自定义的身份认证提供商

流模式

为什么要是用流模式?

前边的例子,都是传输的比较小的数据,基本模式是 客户端请求服务器端响应

如果是传输较大的数据,会带来哪些问题。

  1. 数据包过大导致压力陡增。
  2. 需要等待客户端包全部发送,才能处理以及响应。
posted @ 2021-09-27 23:28  沧海一声笑rush  阅读(89)  评论(0编辑  收藏  举报