网站后台下载图片,海外推广媒体,图片展示网站搭建,vue 企业网站模板目录 1.默认值
2.更新消息
2.1更新规则
2.2保留字段reserved
2.2.1创建通讯录3.0版本---验证错误删除字段造成的数据损坏 2.3未知字段
2.3.1未知字段从哪获取
3.3.2升级通讯录3.1版本--验证未知字段
2.4前后兼容性
3.选项option
3.1选项分类
3.2常用选项列举 1.默认值…目录 1.默认值
2.更新消息
2.1更新规则
2.2保留字段reserved
2.2.1创建通讯录3.0版本---验证错误删除字段造成的数据损坏 2.3未知字段
2.3.1未知字段从哪获取
3.3.2升级通讯录3.1版本--验证未知字段
2.4前后兼容性
3.选项option
3.1选项分类
3.2常用选项列举 1.默认值
反序列化消息时如果被反序列化的二进制序列中不包含某个字段反序列化对象中相应字段时就会设置为该字段的默认值。不同的类型对应的默认值不同: ●对于字符串默认值为空字符串。 ●对于字节默认值为空字节。 ●对于布尔值默认值为false。 对于数值类型默认值为0。 对于枚举默认值是第一个定义的枚举值必须为0。 ●对于消息字段,未设置该字段。它的取值是依赖于语言。 ●对于设置了repeated的字段的默认值是空的(通常是相应语言的一-个空列表)。 对于消息字段、oneof字段 和any字段C和Java语言中都有has_方法来检测当前字段 是否被设置。
2.更新消息
2.1更新规则
如果现有的消息类型已经不再满足我们的需求例如需要扩展一个字段在不破坏任何现有代码的情况下更新消息类型非常简单。遵循如下规则即可: ● 禁止修改任何已有字段的字段编号。 ●若是移除老字段要保证不再使用移除字段的字段编号。正确的做法是保留字段编号 (reserved)以确保该编号将不能被重复使用。不建议直接删除或注释掉字段。 ●int32 uint32 int64 uint64和bool是完全兼容的。可以从这些类型中的一个改为另一个, 而不破坏前后兼容性。若解析出来的数值与相应的类型不匹配会采用与C一致的处理方案; (例如若将64位整数当做32位进行读取它将被截断为32位) ● sint32和sint64相互兼容但不与其他的整型兼容。 ●string和bytes在合法UTF-8字节前提下也是兼容的。 ●bytes包含消息编码版本的情况下嵌套消息与bytes也是兼容的。
●enum与int32, uint32 int64 和uint64兼容(注 意若值不匹配会被截断)。但要注意当反序 列化消息时会根据语言采用不同的处理方案:例如未识别的proto3枚举类型会被保存在消息 中但是当消息反序列化时如何表示是依赖于编程语言的。整型字段总是会保持其的值。 ●oneof: ●将一个单独的值更改为新oneof类型成员之一 是安全和二进制兼容的。 ●若确定没有代码一次性设置多个值那么将多个字段移入一个新oneof类型也是可行的。 ●将任何字段移入已存在的oneof类型是不安全的。
2.2保留字段reserved
如果通过删除或注释掉字段来更新消息类型未来的用户在添加新字段时有可能会使用以前已经 存在但已经被删除或注释掉的字段编号。将来使用该.proto的旧版本时的程序会引发很多问题:数 据损坏、隐私错误等等。 确保不会发生这种情况的一种方法是:使用reserved将指定字段的编号或名称设置为保留项。当 我们再使用这些编号或名称时protocol buffer的编译器将会警告这些编号或名称不可用。
举个例子:
message Message {// 设置保留项reserved 100, 101, 200 to 299;reserved field3, field4;// 注意不要在⼀⾏ reserved 声明中同时声明字段编号和名称。// reserved 102, field5;// 设置保留项之后下⾯代码会告警int32 field1 100; //告警Field field1 uses reserved number 100int32 field2 101; //告警Field field2 uses reserved number 101int32 field3 102; //告警Field name field3 is reservedint32 field4 103; //告警Field name field4 is reserved
}
2.2.1创建通讯录3.0版本---验证错误删除字段造成的数据损坏
现模拟有两个服务他们各自使用一份通讯录.proto文件内容约定好了是一模一样的。 服务1 (service) : 负责序列化通讯录对象并写入文件中。 服务2 (client) :负责读取文件中的数据解析并打印出来。 一段时间后service 更新了自己的.proto文件更新内容为:删除了某个字段并新增了一个字段, 新增的字段使用了被删除字段的字段编号。并将新的序列化对象写进了文件。 但client并没有更新自己的.proto文件。根据结论可能会出现数据损坏的现象接下来就让我们来 验证下这个结论。
新建两个目录: service、 client。 分别存放两个服务的代码。 service目录下新增contacts. proto (通讯录 3.0)
syntax proto3;
package s_contacts;
// 联系⼈
message PeopleInfo {string name 1; // 姓名int32 age 2; // 年龄message Phone {string number 1; // 电话号码
}repeated Phone phone 3; // 电话
}
// 通讯录
message Contacts {repeated PeopleInfo contacts 1;
}
client目录下新增contacts.proto (通讯录 3.0)
syntax proto3;
package c_contacts;
// 联系⼈
message PeopleInfo {string name 1; // 姓名int32 age 2; // 年龄message Phone {string number 1; // 电话号码
}repeated Phone phone 3; // 电话
}
// 通讯录
message Contacts {repeated PeopleInfo contacts 1;
}
分别对两个文件进行编译可自行操作。 继续对service目录下新增service.cc (通讯录3.0) 负责向文件中写通讯录消息内容如下:
#include iostream
#include fstream
#include contacts.pb.husing namespace std;
using namespace s_contacts;
void AddPeopleInfo(PeopleInfo *people)
{cout -------------新增联系⼈------------- endl;cout 请输入联系人姓名;string name;getline(cin, name);people-set_name(name);cout 请输入联系人年龄;int age;cin age;people-set_age(age);cin.ignore(256, \n);for (int i 1;; i){cout 请输入联系人电话 i (输入回车完成电话新增):;string number;getline(cin, number);if (number.empty())break;PeopleInfo_Phone *phone people-add_phone();phone-set_number(number);}cout -------------添加联系人成功------------- endl;
}
int main()
{Contacts contacts;// 先读取已经存在的contacts:fstream input(../contacts.bin, ios::in | ios ::binary);if (!input){cout file not exist,create new file endl;}else if (!contacts.ParseFromIstream(input)){cerr Parse failed! endl;input.close();return -1;}// 向通讯录添加一个联系人AddPeopleInfo(contacts.add_contacts());// 将通讯录写入到本地文件中fstream output(../contacts.bin, ios::out | ios ::binary | ios ::trunc);if (!contacts.SerializePartialToOstream(output)){cerr write failed ! endl;input.close();output.close();return -1;}cout write sucess ! endl;input.close();output.close();return 0;
}service目录下新增makefile
server:server.cc contacts.pb.ccg -o $ $^ -stdc11 -lprotobuf
.PHONY:clean
clean:rm server
client目录下新增client.cc (通讯录3.0) 负责向读出文件中的通讯录消息内容如下:
#include iostream
#include fstream
#include contacts.pb.husing namespace std;
using namespace c_contacts;void PrintContacts(const Contacts contacts)
{for (int i 0; i contacts.contacts_size(); i){const PeopleInfo people contacts.contacts(i);cout ------------联系⼈ i 1 ------------ endl;cout 联系人姓名 people.name() endl;cout 联系人年龄 people.age() endl;int j 1;for (auto phone : people.phone()){cout 电话 j : phone.number();}}
}
int main()
{Contacts contacts;fstream input(../contacts.bin, ios::in | ios ::binary);if (!contacts.ParseFromIstream(input)){cerr Parse failed! endl;input.close();return -1;}// 打印contactsPrintContacts(contacts);input.close();return 0;
}client目录下新增makefile
client:client.cc contacts.pb.ccg -o $ $^ -stdc11 -lprotobuf
.PHONY:clean
clean:rm client
代码编写完成后进行一次读写 确认无误后对service目录下的contacts.proto文件进行更新:删除age字段新增birthday字 段新增的字段使用被删除字段的字段编号。 更新后的contacts.proto (通讯录3.0)内容如下:
syntax proto3;
package s_contacts;
// 联系⼈
message PeopleInfo {string name 1; // 姓名// 删除年龄字段// int32 age 2; // 年龄int32 birthday 2; // ⽣⽇
message Phone {string number 1; // 电话号码
}repeated Phone phone 3; // 电话
}
// 通讯录
message Contacts {repeated PeopleInfo contacts 1;
}
编译文件.proto后还需要更新一下对应的service.cc (通讯录3.0) :
#include iostream
#include fstream
#include contacts.pb.husing namespace std;
using namespace s_contacts;
void AddPeopleInfo(PeopleInfo *people)
{cout -------------新增联系⼈------------- endl;cout 请输入联系人姓名;string name;getline(cin, name);people-set_name(name);// cout 请输入联系人年龄;// int age;// cin age;// people-set_age(age);// cin.ignore(256, \n);cout 请输入联系人生日: ;int birthday;cin birthday;people-set_birthday(birthday);for (int i 1;; i){cout 请输入联系人电话 i (输入回车完成电话新增):;string number;getline(cin, number);if (number.empty())break;PeopleInfo_Phone *phone people-add_phone();phone-set_number(number);}cout -------------添加联系人成功------------- endl;
}
int main()
{Contacts contacts;// 先读取已经存在的contacts:fstream input(../contacts.bin, ios::in | ios ::binary);if (!input){cout file not exist,create new file endl;}else if (!contacts.ParseFromIstream(input)){cerr Parse failed! endl;input.close();return -1;}// 向通讯录添加一个联系人AddPeopleInfo(contacts.add_contacts());// 将通讯录写入到本地文件中fstream output(../contacts.bin, ios::out | ios ::binary | ios ::trunc);if (!contacts.SerializePartialToOstream(output)){cerr write failed ! endl;input.close();output.close();return -1;}cout write sucess ! endl;input.close();output.close();return 0;
}我们对client相关的代码保持原样不进行更新。 再进行一次读写 这时问题便出现了我们发现输入的生日在反序列化时被设置到了使用了相同字段编号的年龄 上! !所以得出结论:若是移除老字段,要保证不再使用移除字段的字段编号不建议直接删除或注 释掉字段。 那么正确的做法是保留字段编号(reserved) 以确保该编号将不能被重复使用。
正确service目录下的contacts.proto写法如下(终版通讯录3.0)。
syntax proto3;
package s_contacts;
// 联系⼈
message PeopleInfo {reserved 2;string name 1; // 姓名// int32 age 2; // 年龄int32 birthday 4;message Phone {string number 1; // 电话号码
}repeated Phone phone 3; // 电话
}
// 通讯录
message Contacts {repeated PeopleInfo contacts 1;
}
编译.proto文件后还需要重新编译下service.cc,让service程序保持使用新生成的pb C文件。 根据实验结果发现‘王五’ 的年龄为0这是由于新增时未设置年龄,通过client程序反序列化 时给年龄字段设置了默认值0。这个结果显然是我们想看到的。 还要解释一下‘ 李四’ 的年龄依旧使用了 之前设置的生日字段‘1221’ 这是因为在新增‘李四’ 的时候生日字段的字段编号依旧为2并且已经被序列化到文件中了。最后再读取的时候字段编号依旧为2。 还要再说一下的是:因为使用了reserved 关键字ProtoBuf在编译阶段就拒绝了我们使用已经保留 的字段编号。到此实验结束也印证了我们的结论。
根据以上的例子有的同学可能还有一-个疑问:如果使用了reserved 2了那么service给‘王五’设置的生日.0120’ , client就没法读到了吗?答案是可以的。继续学习下面的未知字段即可揭晓 2.3未知字段
在通讯录3.0版本中我们向service目录下的contacts.proto新增了‘生日’ 字段但对于client相 关的代码并没有任何改动。验证后发现新代码序列化的消息(service) 也可以被旧代码(client) 解 析。并且这里要说的是新增的‘生日’ 字段在旧程序(client) 中其实并没有丢失而是会作为旧程 序的未知字段。 ●未知字段:解析结构良好的protocol buffer已序列化数据中的未识别字段的表示方式。例如当 旧程序解析带有新字段的数据时这些新字段就会成为旧程序的未知字段。 本来proto3 在解析消息时总是会丢弃未知字段但在3.5版本中重新引入了对未知字段的保留机 制。所以在3.5或更高版本中未知字段在反序列化时会被保留同时也会包含在序列化的结果 中。
2.3.1未知字段从哪获取
了解相关类关系图 MessageLite类介绍(了解) MessageLite从名字看是轻量级的message,仅仅提供序列化、反序列化功能。 ●类定义在google提供的message_lite.h中。 Message类介绍(了解) ●我们自定义的message类都是继承自Message。 ●Message最重要的两个接口GetDescriptor/GetReflection 可以获取该类型对应的Descriptor对象 指针和Reflection对象指针。 ●类定义在google提供的message.h中。
//google::protobuf::Message 部分代码展⽰
const Descriptor* GetDescriptor() const;
const Reflection* GetReflection() const;
Descriptor类介绍(了解) ●Descriptor: 是对message类型定义的描述包括message的名字、 所有字段的描述、原始的 proto文件内容等。 ●类定义在google提供的descriptor.h中。
// 部分代码展⽰
class PROTOBUF_EXPORT Descriptor : private internal::SymbolBase {string name () constint field_count() const;const FieldDescriptor* field(int index) const;const FieldDescriptor* FindFieldByNumber(int number) const;const FieldDescriptor* FindFieldByName(const std::string name) const;const FieldDescriptor* FindFieldByLowercaseName(const std::string lowercase_name) const;const FieldDescriptor* FindFieldByCamelcaseName(const std::string camelcase_name) const;int enum_type_count() const;const EnumDescriptor* enum_type(int index) const;const EnumDescriptor* FindEnumTypeByName(const std::string name) const;const EnumValueDescriptor* FindEnumValueByName(const std::string name) const;
}
Reflection类介绍(了解)
●提供方法来动态访问/修改message中的字段 对每种类型Reflection都提供 了一个单独的接口用于读写字段对应的值。 。针对所有不同的field类型FieldDescriptor: :TYPE_ * ,需要使用不同的Get*() /Set*() /Add*() 接口; 。repeated类型需要使用GetRepeated* () /SetRepeated*() 接口不可以和非repeated 。message对象只可以被由它自身的reflection (message . GetReflection()) 来操 ●类中还包含了访问/修改未知字段的方法。 ●类定义在google提供的message.h中。
// 部分代码展⽰
class PROTOBUF_EXPORT Reflection final
{const UnknownFieldSet GetUnknownFields(const Message message) const;UnknownFieldSet *MutableUnknownFields(Message *message) const;bool HasField(const Message message, const FieldDescriptor *field) const;int FieldSize(const Message message, const FieldDescriptor *field) const;void ClearField(Message *message, const FieldDescriptor *field) const;bool HasOneof(const Message message,const OneofDescriptor *oneof_descriptor) const;void ClearOneof(Message *message,const OneofDescriptor *oneof_descriptor) const;const FieldDescriptor *GetOneofFieldDescriptor(const Message message, const OneofDescriptor *oneof_descriptor) const;// Singular field getters ------------------------------------------// These get the value of a non-repeated field. They return the default// value for fields that arent set.int32_t GetInt32(const Message message, const FieldDescriptor *field) const;int64_t GetInt64(const Message message, const FieldDescriptor *field) const;uint32_t GetUInt32(const Message message,const FieldDescriptor *field) const;uint64_t GetUInt64(const Message message,const FieldDescriptor *field) const;float GetFloat(const Message message, const FieldDescriptor *field) const;double GetDouble(const Message message, const FieldDescriptor *field) const;bool GetBool(const Message message, const FieldDescriptor *field) const;std::string GetString(const Message message,const FieldDescriptor *field) const;const EnumValueDescriptor *GetEnum(const Message message,const FieldDescriptor *field) const;int GetEnumValue(const Message message, const FieldDescriptor *field) const;const Message GetMessage(const Message message,const FieldDescriptor *field,MessageFactory *factory nullptr) const;// Singular field mutators -----------------------------------------// These mutate the value of a non-repeated field.void SetInt32(Message *message, const FieldDescriptor *field,int32_t value) const;void SetInt64(Message *message, const FieldDescriptor *field,int64_t value) const;void SetUInt32(Message *message, const FieldDescriptor *field,uint32_t value) const;void SetUInt64(Message *message, const FieldDescriptor *field,uint64_t value) const;void SetFloat(Message *message, const FieldDescriptor *field,float value) const;void SetDouble(Message *message, const FieldDescriptor *field,double value) const;void SetBool(Message *message, const FieldDescriptor *field,bool value) const;void SetString(Message *message, const FieldDescriptor *field,std::string value) const;void SetEnum(Message *message, const FieldDescriptor *field,const EnumValueDescriptor *value) const;void SetEnumValue(Message *message, const FieldDescriptor *field,int value) const;Message *MutableMessage(Message *message, const FieldDescriptor *field,MessageFactory *factory nullptr) const;PROTOBUF_NODISCARD Message *ReleaseMessage(Message *message, const FieldDescriptor *field,MessageFactory *factory nullptr) const;// Repeated field getters ------------------------------------------// These get the value of one element of a repeated field.int32_t GetRepeatedInt32(const Message message, const FieldDescriptor *field,int index) const;int64_t GetRepeatedInt64(const Message message, const FieldDescriptor *field,int index) const;uint32_t GetRepeatedUInt32(const Message message,const FieldDescriptor *field, int index) const;uint64_t GetRepeatedUInt64(const Message message,const FieldDescriptor *field, int index) const;float GetRepeatedFloat(const Message message, const FieldDescriptor *field,int index) const;double GetRepeatedDouble(const Message message, const FieldDescriptor *field,int index) const;bool GetRepeatedBool(const Message message, const FieldDescriptor *field,int index) const;std::string GetRepeatedString(const Message message,const FieldDescriptor *field, int index) const;const EnumValueDescriptor *GetRepeatedEnum(const Message message,const FieldDescriptor *field,int index) const;int GetRepeatedEnumValue(const Message message, const FieldDescriptor *field,int index) const;const Message GetRepeatedMessage(const Message message,const FieldDescriptor *field,int index) const;const std::string GetRepeatedStringReference(const Message message,const FieldDescriptor *field,int index,std::string *scratch) const;// Repeated field mutators -----------------------------------------// These mutate the value of one element of a repeated field.void SetRepeatedInt32(Message *message, const FieldDescriptor *field,int index, int32_t value) const;void SetRepeatedInt64(Message *message, const FieldDescriptor *field,int index, int64_t value) const;void SetRepeatedUInt32(Message *message, const FieldDescriptor *field,int index, uint32_t value) const;void SetRepeatedUInt64(Message *message, const FieldDescriptor *field,int index, uint64_t value) const;void SetRepeatedFloat(Message *message, const FieldDescriptor *field,int index, float value) const;void SetRepeatedDouble(Message *message, const FieldDescriptor *field,int index, double value) const;void SetRepeatedBool(Message *message, const FieldDescriptor *field,int index, bool value) const;void SetRepeatedString(Message *message, const FieldDescriptor *field,int index, std::string value) const;void SetRepeatedEnum(Message *message, const FieldDescriptor *field,int index, const EnumValueDescriptor *value) const;void SetRepeatedEnumValue(Message *message, const FieldDescriptor *field,int index, int value) const;Message *MutableRepeatedMessage(Message *message,const FieldDescriptor *field,int index) const;// Repeated field adders -------------------------------------------// These add an element to a repeated field.void AddInt32(Message *message, const FieldDescriptor *field,int32_t value) const;void AddInt64(Message *message, const FieldDescriptor *field,int64_t value) const;void AddUInt32(Message *message, const FieldDescriptor *field,uint32_t value) const;void AddUInt64(Message *message, const FieldDescriptor *field,uint64_t value) const;void AddFloat(Message *message, const FieldDescriptor *field,float value) const;void AddDouble(Message *message, const FieldDescriptor *field,double value) const;void AddBool(Message *message, const FieldDescriptor *field,bool value) const;void AddString(Message *message, const FieldDescriptor *field,std::string value) const;void AddEnum(Message *message, const FieldDescriptor *field,const EnumValueDescriptor *value) const;void AddEnumValue(Message *message, const FieldDescriptor *field,int value) const;Message *AddMessage(Message *message, const FieldDescriptor *field,MessageFactory *factory nullptr) const;const FieldDescriptor *FindKnownExtensionByName(const std::string name) const;const FieldDescriptor *FindKnownExtensionByNumber(int number) const;bool SupportsUnknownEnumValues() const;
};
UnknownFieldSet类介绍(重要) ●UnknownFieldSet 包含在分析消息时遇到但未由其类型定 义的所有字段。 ●若要将 UnknownFieldSet附加到任何消息,请调用Reflection::GetUnknownFields()。 ●类定义在unknown_ field_ set.h 中。
class PROTOBUF_EXPORT UnknownFieldSet
{inline void Clear();void ClearAndFreeMemory();inline bool empty() const;inline int field_count() const;inline const UnknownField field(int index) const;inline UnknownField *mutable_field(int index);// Adding fields ---------------------------------------------------void AddVarint(int number, uint64_t value);void AddFixed32(int number, uint32_t value);void AddFixed64(int number, uint64_t value);void AddLengthDelimited(int number, const std::string value);std::string *AddLengthDelimited(int number);UnknownFieldSet *AddGroup(int number);// Parsing helpers -------------------------------------------------// These work exactly like the similarly-named methods of Message.bool MergeFromCodedStream(io::CodedInputStream *input);bool ParseFromCodedStream(io::CodedInputStream *input);bool ParseFromZeroCopyStream(io::ZeroCopyInputStream *input);bool ParseFromArray(const void *data, int size);inline bool ParseFromString(const std::string data){return ParseFromArray(data.data(), static_castint(data.size()));}// Serialization.bool SerializeToString(std::string *output) const;bool SerializeToCodedStream(io::CodedOutputStream *output) const;static const UnknownFieldSet default_instance();
};
UnknownField类介绍(重要) ●表示未知字段集中的一个字段。 ●类定义在unknown_ field_set.h 中。
class PROTOBUF_EXPORT UnknownField
{
public:enum Type{TYPE_VARINT,TYPE_FIXED32,TYPE_FIXED64,TYPE_LENGTH_DELIMITED,TYPE_GROUP};inline int number() const;inline Type type() const;// Accessors -------------------------------------------------------// Each method works only for UnknownFields of the corresponding type.inline uint64_t varint() const;inline uint32_t fixed32() const;inline uint64_t fixed64() const;inline const std::string length_delimited() const;inline const UnknownFieldSet group() const;inline void set_varint(uint64_t value);inline void set_fixed32(uint32_t value);inline void set_fixed64(uint64_t value);inline void set_length_delimited(const std::string value);inline std::string *mutable_length_delimited();inline UnknownFieldSet *mutable_group();
};
3.3.2升级通讯录3.1版本--验证未知字段
更新client.cc (通讯录3.1),在这个版本中需要打印出未知字段的内容。更新的代码如下: //打印未知字段const Reflection* reflection PeopleInfo::GetReflection();const UnknownFieldSet unknowSet reflection-GetUnknownFields(people);for (int j 0; j unknowSet.field_count(); j) {const UnknownField unknow_field unknowSet.field(j);cout 未知字段 j1 : 字段编号: unknow_field.number() 类型: unknow_field.type();switch (unknow_field.type()) {case UnknownField::Type::TYPE_VARINT:cout 值: unknow_field.varint() endl;break;case UnknownField::Type::TYPE_LENGTH_DELIMITED:cout 值: unknow_field.length_delimited() endl;break;}}
其他文件均不用做任何修改重新编译client.cc,进行一次读操作可得如下结果: 类型为何为 0 在介绍 UnknownField 类中讲到了类中包含了未知字段的几种类型
enum Type {TYPE_VARINT,TYPE_FIXED32,TYPE_FIXED64,TYPE_LENGTH_DELIMITED,TYPE_GROUP
};
类型为 0即为 TYPE_VARINT。
2.4前后兼容性
根据上述的例子可以得出pb是具有向前兼容的。为了叙述方便把增加了“生日”属性的service 称为‘新模块” ;未做变动的client称为“老模块” 。 ●向前兼容:老模块能够正确识别新模块生成或发出的协议。这时新增加的“生日”属性会被当作未 知字段(pb 3.5版本及之后)。 ●向后兼容:新模块也能够正确识别老模块生成或发出的协议。 前后兼容的作用:当我们维护- -个很庞大的分布式系统时由于你无法同时升级所有模块,为了保证 在升级过程中整个系统能够尽可能不受影响就需要尽量保证通讯协议的“向后兼容”或“向前兼 容”。
3.选项option
.proto文件中可以声明许多选项使用option 标注。选项能影响proto编译器的某些处理方式。
3.1选项分类
选项的完整列表在google/protobuf/descriptor.proto中定义。部分代码:
syntax proto2; // descriptor.proto 使⽤ proto2 语法版本
message FileOptions { ... } // ⽂件选项 定义在 FileOptions 消息中
message MessageOptions { ... } // 消息类型选项 定义在 MessageOptions 消息中
message FieldOptions { ... } // 消息字段选项 定义在 FieldOptions 消息中
message OneofOptions { ... } // oneof字段选项 定义在 OneofOptions 消息中
message EnumOptions { ... } // 枚举类型选项 定义在 EnumOptions 消息中
message EnumValueOptions { .. } // 枚举值选项 定义在 EnumValueOptions 消息中
message ServiceOptions { ... } // 服务选项 定义在 ServiceOptions 消息中
message MethodOptions { ... } // 服务⽅法选项 定义在 MethodOptions 消息中
...
由此可见选项分为文件级、消息级、字段级等等但并没有-种选项能作用于所有的类型。
3.2常用选项列举
●optimize_for :该选项为文件选项可以设置protoc编译器的优化级别分别为SPEED、 CODE_ SIZE、 LITE_ RUNTIME。 受该选项影响设置不同的优化级别编译.proto文件后生 成的代码内容不同。 SPEED : protoc编译器将生成的代码是高度优化的代码运行效率高但是由此生成的代码 编译后会占用更多的空间。SPEED 是默认选项。 CODE_ SIZE : proto编译器将生成最少的类会占用更少的空间是依赖基于反射的代码来 实现序列化、反序列化和各种其他操作。但和SPEED恰恰相反它的代码运行效率较低。这 种方式适合用在包含大量的.proto文件但并不盲目追求速度的应用中。
LITE_ RUNTIME :生成的代码执行效率高同时生成代码编译后的所占用的空间也是非常 少。这是以牺牲Protocol Buffer提供的反射功能为代价的仅仅提供encoding序列化功能, 所以我们在链接BP库时仅需链接libprotobuf-lite,而非libprotobuf。 这种模式通常用于资源 有限的平台例如移动手机平台中。
option optimize_for LITE_RUNTIME;
allow_alias :允许将相同的常量值分配给不同的枚举常量用来定义别名。该选项为枚举选项。 举个例子:
enum PhoneType {option allow_alias true;MP 0;TEL 1;LANDLINE 1; // 若不加 option allow_alias true; 这⼀⾏会编译报错
}