Algunas notas sobre protobuf

Después de leer algunos documentos de protobuf, escriba algo para recordar.

https://github.com/google/protobuf/blob/master/CHANGES.txt

Google lanzó protobuf v3. Para hacer que pb sea más utilizable y en varios idiomas, realizó los siguientes cambios en protobuf v2:

      1. Eliminación de la lógica de presencia de campo para campos de valores primitivos (increíble, guárdelo para traducirlo más adelante), elimine requerido (totalmente de acuerdo, es decir, mantenga repetido, requerido y opcional, el valor predeterminado es opcional), elimine el valor predeterminado (no está claro ). Google generó estos cambios para ser mejor compatibles con los lenguajes Android Java, Objective C y Go;

      2. Eliminar la compatibilidad con campos desconocidos;

      3. La herencia ya no se admite y se reemplaza por Cualquier tipo;

      4 Se corrigió el tipo desconocido en enum;

      5 soportes mapa;

         Tanto protobuf v2 como v3 admiten mapas, y su formulario de declaración es el siguiente: 

         mensaje Foo {

                mapa<cadena, cadena> valores = 1;

         }

         Tenga en cuenta que el mapa aquí es unordered_map.

      6  Se agregaron algunos conjuntos de tipos para admitir la expresión de tiempo, datos dinámicos, etc.;

      7 De forma predeterminada, está codificado en formato json en lugar de binario.

Actualmente, la versión alfa v3 solo implementa cinco funciones 1 a 5, y 6 y 7 aún no son compatibles. La palabra clave de sintaxis se agregó recientemente para indicar la versión del protocolo protobuf del archivo proto. Si no se especifica, es v2. como:

 // foo.proto

      sintaxis = "proto3";

      barra de mensajes {...}

Si actualmente está utilizando la versión 2, no se le permite cambiar a la versión 3 por el momento. También brindaremos soporte para la versión 2. Si eres novato, no dudes en utilizar la v3.



https://github.com/golang/protobuf :

La implementación de protobuf de 1 go no es compatible con RPC.

2. El protobuf de Go implementa un complemento go protoc-gen-go, que debe colocarse en $GOBIN y de forma predeterminada en $GOPATH/bin. También debe estar en $PATH para que el compilador de protocolos lo encuentre. protoc compila archivos proto en archivos de código fuente go, cuyo sufijo de nombre es .pb.go. El formato del comando protoc es el siguiente:

protocolo --go_out=. *.proto

3. Algunas cuestiones sobre el código fuente de .pb.go son las siguientes:

  - Los nombres de variables utilizan la nomenclatura camel, como camel_case que se compila en CamelCase.

  - El método set no se generará para el campo, simplemente asignará valores a los miembros directamente. 

  - No hay un definidor, pero hay un método getter. Si el campo se establece en un valor, se devuelve el valor establecido. Si no se establece, devuelve el valor predeterminado. Si no se recibe ningún mensaje, se devuelve nil.

  - El valor inicial de todos los miembros de la estructura es cero, si desea asignar un valor a sus miembros, debe ser antes de la serialización. No tiene sentido modificar los valores de los miembros de la estructura después de la serialización.

  - Reset() de struct borrará los valores de todos los campos de la estructura a cero.

  - 非repeated的field成员的类型都是指针类型,当它为空时,意味着其值为空。如"required field int32 f "或者"optional field int32 f "被编译后的类型都是F *int32。

  - Repeated类型的fields被编译后则是slices.

  - 与其他语言一样,go会生成Helper函数,以便于设置field的值。针对获取值得Helper函数不再建议使用。                        msg.Foo = proto.String("hello") // set field

  - 一个field如果有default值,则这个值会被编译为一个常量,其名称的规则为Default_StructName_FieldName,而且相关的Get方法会默认返回这个值。不见直接用这些const值。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
message Test {
     optional int32 type = 2 [ default =77];
     extensions 100 to 199;
}
 
type Test  struct  {
     Type             *int32                    `protobuf: "varint,2,opt,name=type,def=77"  json: "type,omitempty" `
}
 
 
const  Default_Test_Type int32 = 77
     
func (m *Test) GetType() int32 {
     if  m != nil && m.Type != nil {
         return  *m.Type
     }
     return  Default_Test_Type
}


  -  Enum类型会对类型名称和其值分别处理,其值的Enum名称以类型做前缀,每个field的名称和值会形成map,可以互相查找。

?
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
enum  FOO {
     X = 17; 
     Y = 18; 
};
==>
type FOO int32
 
const  (
     FOO_X FOO = 17
     FOO_Y FOO = 18
)
 
var FOO_name = map[int32]string{
     17:  "X" ,
     18:  "Y" ,
}
var FOO_value = map[string]int32{
     "X" : 17, 
     "Y" : 18, 
}
 
func (x FOO) Enum() *FOO {
     p :=  new (FOO)
     *p = x 
     return  p
}
func (x FOO) String() string {
     return  proto.EnumName(FOO_name, int32(x))
}
func (x *FOO) UnmarshalJSON(data []byte) error {
     value, err := proto.UnmarshalJSONEnum(FOO_value, data,  "FOO" )
     if  err != nil {
         return  err
     }
     *x = FOO(value)
     return  nil
}

  - 如果group和enum是内置在message中的,则其名称的前缀会有message的名称。 

  - Extensions会被编译为一个变量,其名称开头为E_。extension相关的方法有 HasExtension, ClearExtension, GetExtension, SetExtension .

  - 序列化方法有Marshal和Unmarshal.





https://developers.google.com/protocol-buffers/docs/proto:

1 default value。bool的默认值是false,数值的默认值是0,enum的默认值是其第一个元素,string的默认值是空字符串。

 2 tag id. id 1-15占用1个字节,16到2047占用两个字节。所以1-15要留个频繁使用的字段,不要刚开始定义字段的是时候都分配出去。

   tag值最小是1, 最大是(2^29 - 1)即536,870,911,但是要避开19000到19999,这是protobuf内置的类型要用到的tag id。 

 3 enum可以有alias。enum的值不能为负数。

 enum EnumAllowingAlias {

  option allow_alias = true;

  UNKNOWN = 0;

  STARTED = 1;

  RUNNING = 1;

}

enum EnumNotAllowingAlias {

  UNKNOWN = 0;

  STARTED = 1;

  // RUNNING = 1;  // Uncommenting this line will cause a compile error inside Google and a warning message outside.

}

 4 当协议更新的时候,如果某个字段过时了,就可以更改field的name,如"OBSOLETE_xxx",以告诉使用者不要在使用这个field。

   也可以更改field type,前提是tag id不变,这些类型就是兼容的(之所以能兼容,按照我的理解,一个field就是一个kay-value对,tag id为key,value即为值,而type只是在序列化和反序列化的时候起到解释值的作用,并无其他作用;key = (tag << 3) | wire_type,即类型占用最多3个字节,所以有上面的tag范围是2^29 - 1)。如下几个类型是兼容的:

   A int32 uint32 int64 uint64 bool    

   B sint32 siint64

   C string bytes (字符类型是UTF8)

   D fixed32 sfixed32 fixed64 sfixed64

   E optional repeated

   F default([default = value])value也可以被修改

 5 protoc --proto-path= --cpp_out= --java_out= file.proto

   --proto-path可以被-I代替。

 6 protobuf对repeated压缩不够好,所以尽量在后面加上[packed = true]。

 7 序列化的时候不能把多个message序列化后的内容放在一起发出去,尽量以len1 + msg1 + len2 + msg2这种形式发送。

 8 不要让protobuf对象成为全局变量或者类成员,因为其clear方法只会把占用的内存空间清零,而不会释放,使得进程空间越来越大,可参考《Protobuf使用不当导致的程序内存上涨问题》。 



https://developers.google.com/protocol-buffers/docs/encoding:

1 正常field的kv对的编码顺序是:1 小端序;2 varints,即每7个bit为一个byte,在byte的第一个bit赋值1,最后一个byte的第一个bit赋值0;3 对field的tag加type构成key,值为value,即((tag_id << 3) | wire_type) + value。但是针对repeated这种,其结果为((tag_id << 3) | wire_type) + len + value,长度len为array size,prootbuf解析度时候,会根据wire_type为2知道这是个array。


2 如果序列化的时候,多个kv对用了一个tag id,则这个kv对应的field的值为最后一次出现的kv对的值。

3 repeated的[packed = true]只有type为数值的时候才能用。


https://github.com/google/protobuf/tree/v3.0.0-alpha-1

1 安装步骤:

  

$ ./configure --prefix=/home/user/bin/
$ make
$ make check
$ make install


2 如果不想生成动态protobuf库,则用命令./configure --disable-shared。


版权所有,禁止转载。

Supongo que te gusta

Origin blog.csdn.net/menggucaoyuan/article/details/43602915
Recomendado
Clasificación