Protoc 文件
安装 protoc 工具
prootc
工具,核心是使用 c++
写的,常见的安装方式,有三种
- 使用
ubuntu
系统自带的包管理工具 apt 安装:sudo apt install protobuf-compiler
- 下载
c++
源码编译安装 - 下载官网编译好的二进制文件安装
https://github.com/protocolbuffers/protobuf/releases
以上三种方法,第一种最简单,但是安装的往往不是最新版本,第二种过于复杂,本文推荐使用第三种方式安装部署
使用 wget https://github.com/protocolbuffers/protobuf/releases/download/v28.3/protoc-28.3-linux-x86_64.zip 下载安装包并解压,将 /bin/protoc 放到 /usr/local/bin 下,将 include 下的 google 文件,放到 /usr/local/include 目录下
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的声明周期
-
请求只能是
client
来发送,不能由server
发起。 -
但是
server
发送的可以直接发送。 -
gGRPC是自带身份认证的。其中包括
4种身份认证机制
- 不采取任何认证的连接
- TLS/SSL 连接
- 基于 Google Token 的身份认证
- 自定义的身份认证提供商
流模式
为什么要是用流模式?
前边的例子,都是传输的比较小的数据,基本模式是 客户端请求
、服务器端响应
如果是传输较大的数据,会带来哪些问题。
- 数据包过大导致压力陡增。
- 需要等待客户端包全部发送,才能处理以及响应。