快乐冲浪与生活

多体验、多体会、多体悟

0%

Protocol Buffer(Protobuf) 是一种高效的数据结构序列化的机制,同时也是一种结构化数据的存储格式。

序列化与反序列化

  • 序列化:将数据结构或对象转换成二进制串的过程;
  • 反序列化:将序列化后的二进制串转换成数据结构或对象的过程;

语法

  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
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
/*
* 语法
*/

/*
* 指定 Protobuf 解析使用的版本,可以是 proto3 或 proto2
*/
syntax = "proto3";

/*
* message 定义中的每一个字段都有一个唯一标识,该标识用于在二进制格式中识别字段
* 字段的标识一旦使用就不要进行修改
* 当标识为 1 到 15 时,使用一个字节进行编码,字节信息中包含字段的标识以及类型
* 当标识为 16 到 2047 时,使用两个字节进行编码
* Field numbers in the range 16 through 2047 take two bytes. So you should reserve the numbers 1 through 15 for very frequently occurring message elements.
* 在编码的过程中,标识使用应当留有余地,便于将来扩展
* 标识最小的值是 1,最大的值为 2^29-1
* 不能使用的标识为 19000 到 19999
* 不能再使用已经被 reserved 的标识
*/

/*
定义 message 的语法:
    message ${MessageName} {
        ${Scalar Value Type} ${FieldName1} = ${Tag Number1};
                .
                .
                .
        ${Scalar Value Type} ${FieldNameN} = ${Tag NumberN};
    }
*/

message MessageTypes {
    /*
    * 标量值类型
    */
    string stringType = 1; // 字符串可以是 UTF-8 编码,也可以是一个 7 比特的 ASCII 字符,默认为“”
    // 数值类型,默认为 0
    int32 int32Type = 2; // 使用变量长度进行编码,如果是负数,请使用 sint32
    int64 int64Type = 3; // 使用变量长度进行编码,如果是负数,请使用 sint64
    uint32 uInt32Type = 4; // 使用变量长度进行编码
    uint64 uInt64Type = 5; // 使用变量长度进行编码
    sint32 sInt32Type = 6; // 使用变量长度进行编码,处理负数更高效
    sint64 sInt64Type = 7; // 使用变量长度进行编码,处理负数更高效

    fixed32 fixed32Type = 8; // 变量总是占 4 个字节,当值大于 2^28 时,比使用 uint32 更有效率
    fixed64 fixed64Type = 9; // 变量总是占 8 个字节,当值大于 2^56 时,比使用 uint64 更有效率

    sfixed32 sfixed32Type = 10; // 变量总是占 4 个字节
    sfixed64 sfixed64Type = 11; // 变量总是占 8 个字节

    bool boolType = 12; // 布尔类型,默认为 false

    bytes bytesType = 13; // 可包含任意长度的字节数组,默认为长度为 0 的字节数组

    double doubleType = 14;
    float floatType = 15;

    enum Week {
        UNDEFINED = 0; // 第 1 个值
        SUNDAY = 1;
        MONDAY = 2;
        TUESDAY = 3;
        WEDNESDAY = 4;
        THURSDAY = 5;
        FRIDAY = 6;
        SATURDAY = 7;
    }
    Week wkDayType = 16;

    /*
    * 定义标量值类型的集合
    * Syntax: repeated ${ScalarType} ${name} = TagValue
    */
    repeated string listOfString = 17; // List[String]
}

/*
* 在其它 message 中使用已定义的 message
*/
message Person {
    string fname = 1;
    string sname = 2;
}

message City {
    Person p = 1;
}

/*
* 嵌套的 message 定义
*/
message NestedMessages {
    message FirstLevelNestedMessage {
        string firstString = 1;
        message SecondLevelNestedMessage {
            string secondString = 2;
        }
    }
    FirstLevelNestedMessage msg = 1;
    FirstLevelNestedMessage.SecondLevelNestedMessage msg2 = 2;
}

/*
* .proto 文件的引入
*/

// one.proto
// message One {
//     string oneMsg = 1;
// }

// two.proto
//  import "myproject/one.proto"
//  message Two {
//       string twoMsg = 2;
//  }


/*
* 高级知识点
*/

/*
* message 发生改变时,永远不要修改或使用已经删除字段的标识
*/

/*
* 使用 reserved 保留已删除的标识或字段名
*/
message ReservedMessage {
    reserved 0, 1, 2, 3 to 10; // 这里的标识不可再使用
    reserved "firstMsg", "secondMsg", "thirdMsg"; // 这里的字段名不可再使用
}

/*
* 引用其它文件中定义的 message
*/
import "google/protobuf/any.proto";
message AnySampleMessage {
    repeated google.protobuf.Any.details = 1;
}

/*
*  OneOf
* 相同于 union,只能是其中一个
* 使用 oneof 的 message 不能被 repeated
*/
message OneOfMessage {
    oneof msg {
        string fname = 1;
        string sname = 2;
    };
}

/*
* Maps
* map 字段不能被 repeated
*/
message MessageWithMaps {
    map<string, string> mapOfMessages = 1;
}


/*
* Packages
* 声明一个包名,防止同名的 message
* 语法:
    package ${packageName};

    访问方式
    ${packageName}.${messageName} = ${tagNumber};
*/

/*
* 在 RPC 系统中使用,其中可以定义方法
*/
message SearchRequest {
    string queryString = 1;
}

message SearchResponse {
    string queryResponse = 1;
}
service SearchService {
    rpc Search (SearchRequest) returns (SearchResponse);
}

数据类型

Protobuf 内置的数据类型以及在 Go 中对应的数据类型:

Redis 集群是基于“主从复制”特性之上的分布式 Redis 版本,可提供高并发、高性能、高可用的数据库服务。Redis 集群突破了单台服务器的内存局限,集群中的每一个节点都可以存储数据,同时维护着 “key-node” 的映射表。本文记录了 3 主 3 从的 Redis 集群的配置过程,主要内容包括:

  1. Redis 集群的配置过程;
  2. 集群相关命令;
  3. Go 存取集群数据;

每当有快速绘制图表的需求时,第一时间反应到的肯定是 Matplotlib,因为其官方提供了详细的 API 文档及示例。但是每次在编码时,总是时不时地需要查看文档,不利用于可视化快速成型。所以在本文中罗列一些 bar 图的快速实现,方便 Ctrl+C/V。

Revel 是一个以高效率、高性能著称的 Go Web 框架,提供了路由、参数解析和验证、会话机制、模板机制、缓存和任务管理等诸多常用的 Web 开发功能。同时作为一个全栈的 MVC 框架, Revel 通过模块实现了组件的复用,因此可以大大提高开发者的效率。其高性能则是依托 Go 语言的性能,相信这个不必多说。但相较于其它职责相对单一的 Web 框架(如 Gin、go-restful),Revel 只能说是在保证性能的基础上尽可能地对开发者友好。

Redis 主从复制可以实现数据库的读写分离,即主节点负责接收写请求、从节点负责接收读请求,是高性能 Redis 服务的基础。所以配置 Redis 主从复制应当作为开发者的技能之一,后文内容包括:

  1. 单机配置一主二从的主从复制服务
  2. 服务验证;

同源策略(Same-Origin Policy,SOP)是一种保护 Web 资源的安全机制,它限制了不同源之间的资源访问。需要说明的是,SOP 只作用于应用脚本,这意味着在 HTML 标签中可以引入不同源的图片、CSS 文件或动态加载的脚本文件(见 验证 1 )。