Google出品的Protocol Buffer,别只会用Json和XML了
来源:互联网 发布:网络语言兔子 编辑:程序博客网 时间:2024/05/16 11:30
原文地址:http://blog.csdn.net/carson_ho/article/details/70037693
前言
- 习惯用
Json、XML
数据存储格式的你们,相信大多都没听过Protocol Buffer
Protocol Buffer
其实 是Google
出品的一种轻量 & 高效的结构化数据存储格式,性能比Json、XML
真的强!太!多!由于
Google
出品,我相信Protocol Buffer
已经具备足够的吸引力- 今天,我将献上一份
Protocol Buffer
的介绍 & 使用攻略,希望你们会喜欢。
目录
1. 定义
一种 结构化数据 的数据存储格式(类似于 `XML、Json` )
Protocol Buffer
目前有两个版本:proto2
和proto3
- 因为
proto3
还是beta 版,所以本次讲解是proto2
2. 作用
通过将 结构化的数据 进行 串行化(**序列化**),从而实现 **数据存储 / RPC 数据交换**的功能
- 序列化: 将 数据结构或对象 转换成 二进制串 的过程
- 反序列化:将在序列化过程中所生成的二进制串 转换成 数据结构或者对象 的过程
3. 特点
- 对比于 常见的
XML、Json
数据存储格式,Protocol Buffer
有如下特点:
4. 应用场景
传输数据量大 & 网络环境不稳定 的数据存储、RPC 数据交换 的需求场景
如 即时IM (QQ、微信)的需求场景
总结
在 传输数据量较大的需求场景下,Protocol Buffer
比XML、Json
更小、更快、使用 & 维护更简单!
5. 使用流程
使用 Protocol Buffer
的流程如下:
5.1 环境配置
要使用
Protocol Buffer
,需要先在电脑上安装Protocol Buffer
整个 安装过程 只需要按照以下步骤进行即可:
整个安装过程请 自备梯子 以保证 网络畅通
步骤1:下载 Protocol Buffer
安装包
- 下载方式1:官网下载(需要翻墙)
- 下载方式2:贴心的我 已经给你们准备好了,请移步百度网盘,密码:paju
此处选择 较稳定的版本
protobuf-2.6.1.tar.gz
进行演示
下载成功后,对文件进行解压,如下图:
步骤2:安装 HOMEBREW
(已安装的可以跳过)
- 1
- 2
- 1
- 2
步骤3:安装 Protocol Buffer
打开 您的终端 依次输入 下列指令 即可:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
步骤4:检查 Protocol Buffer
是否安装成功
- 1
- 2
- 1
- 2
出现 libprotoc 2.6.1
提示即表示 安装成功,如下图
特别注意:
protoc
=Protocol Buffer
的编译器- 作用:将
.proto文件
编译成对应平台的 头文件和源代码文件 - 在下面会详细介绍
至此, Protocol Buffer
已经安装完成。下面将讲解如何具体使用Protocol Buffer
5.2 构建 Protocol Buffer
消息对象模型
5.2.1 构建步骤
下面将通过一个实例(Android(Java)
平台为例)详细介绍每个步骤。
5.2.2 详细介绍
- 实例说明:构建一个
Person类
的数据结构,包含成员变量name、id、email
等等
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 平台使用:以
Android(Java)
平台为例来进行演示
步骤1:通过 Protocol Buffer
语法 描述 需要存储的数据结构
- 新建一个文件,命名规则为:文件名 = 类名,后缀为
.proto
此处叫
Demo.proto
- 根据上述数据结构的需求,在
Demo.proto
里 通过Protocol Buffer
语法写入对应.proto
对象模型的代码,如下:
- 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
- 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
- 下面将结合 上述例子 对
Protocol Buffer
语法 进行详细介绍
关注1:包名
- 1
- 2
- 1
- 2
- 作用:防止不同
.proto
项目间命名 发生冲突 Protocol buffer
包的解析过程如下:Protocol buffer
的类型名称解析与C++
一致:从 最内部 开始查找,依次 向外 进行每个包会被看作是其父类包的内部类
Protocol buffer
编译器会解析.proto
文件中定义的所有类型名- 生成器会根据 不同语言 生成 对应语言 的代码文件
a. 即对 不同语言 使用了 不同的规则 进行处理
b.Protoco Buffer
提供C++、Java、Python
三种语言的 API
关注2:Option选项
- 1
- 2
- 3
- 1
- 2
- 3
作用:影响 特定环境下 的处理方式
但不改变整个文件声明的含义
常用Option选项如下:
- 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
- 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
- 在
ProtocolBuffers
中允许 自定义选项 并 使用 - 该功能属于高级特性,使用频率很低,此处不过多描述。有兴趣可查看官方文档
关注3:消息模型
- 作用:真正用于描述 数据结构
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 组成:在
ProtocolBuffers
中:- 一个
.proto
消息模型 = 一个.proto
文件 = 消息对象 + 字段 - 一个消息对象(
Message
) = 一个 结构化数据 - 消息对象(
Message
)里的 字段 = 结构化数据 里的成员变量
- 一个
下面会详细介绍 .proto
消息模型里的 消息对象 & 字段
1. 消息对象
在 ProtocolBuffers
中:
- 一个消息对象(
Message
) = 一个 结构化数据 - 消息对象用 修饰符
message
修饰 - 消息对象 含有 字段:消息对象(
Message
)里的 字段 = 结构化数据 里的成员变量
特别注意:
a. 添加:在一个 .proto文件 中可定义多个 消息对象
- 应用场景:尽可能将与 某一消息类型 对应的响应消息格式 定义到相同的
.proto
文件 中 - 实例:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
b. 一个消息对象 里 可以定义 另外一个消息对象(即嵌套)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
2. 字段
- 消息对象的字段 组成主要是:字段 = 字段修饰符 + 字段类型 +字段名 +标识号
- 下面将对每一项详细介绍
a. 字段修饰符
- 作用:设置该字段解析时的规则
- 具体类型如下:
b. 字段类型
字段类型主要有 三 类:
- 基本数据 类型
- 枚举 类型
- 消息对象 类型
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
1. 基本数据类型
.proto
基本数据类型 对应于 各平台的基本数据类型如下:
2. 枚举类型
- 作用:为字段指定一个 可能取值的字段集合
该字段只能从 该指定的字段集合里 取值
- 说明:如下面例子,电话号码 可能是手机号、家庭电话号或工作电话号的其中一个,那么就将
PhoneType
定义为枚举类型,并将加入电话的集合(MOBILE
、HOME
、WORK
)
- 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
- 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
额外说明
当对一个 使用了枚举类型的.proto
文件 使用 Protocol Buffer
编译器编译时,生成的代码文件中:
- 对
Java 或 C++
来说,将有一个对应的enum
文件 - 对
Python
来说,有一个特殊的EnumDescriptor
类
被用来在运行时生成的类中创建一系列的整型值符号常量(symbolic constants)
3. 消息对象 类型
一个消息对象 可以将 其他消息对象类型 用作字段类型,情况如下:
3.1 使用同一个 .proto 文件里的消息类型
a. 使用 内部消息类型
目的:先在 消息类型 中定义 其他消息类型 ,然后再使用
即嵌套,需要 用作字段类型的 消息类型 定义在 该消息类型里
实例:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
b. 使用 外部消息类型
即外部重用,需要 用作字段类型的消息类型 定义在 该消息类型外部
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
c. 使用 外部消息的内部消息类型
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
3.2 使用不同 .proto 文件里的消息类型
- 目的:需要在
A.proto
文件 使用B.proto
文件里的消息类型 - 解决方案:在
A.proto
文件 通过导入(import
)B.proto
文件中来使用B.proto
文件 里的消息类型
- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
当然,在使用 不同 .proto
文件里的消息类型 时 也会存在想 使用同一个 .proto
文件消息类型的情况,但使用都是一样,此处不作过多描述。
3.3 将 消息对象类型 用在 RPC(远程方法调用)系统
- 解决方案:在
.proto
文件中定义一个RPC
服务接口,Protocol Buffer
编译器会根据所选择的不同语言平台 生成服务接口代码 - 由于使用得不多,此处不作过多描述,具体请看该文档
c. 字段名
该字段的名称,此处不作过多描述。
d. 标识号
作用:通过二进制格式唯一标识每个字段
- 一旦开始使用就不能够再改变
- 标识号使用范围:[1,2的29次方 - 1]
- 不可使用 [19000-19999] 标识号, 因为
Protobuf
协议实现中对这些标识号进行了预留。假若使用,则会报错
编码占有内存规则:
每个字段在进行编码时都会占用内存,而 占用内存大小 取决于 标识号:- 范围 [1,15] 标识号的字段 在编码时占用1个字节;
- 范围 [16,2047] 标识号的字段 在编码时占用2个字节
使用建议
- 为频繁出现的 消息字段 保留 [1,15] 的标识号
- 为将来有可能添加的、频繁出现的 消息字段预留 [1,15] 标识号
关于 字段 的高级用法
1. 更新消息对象 的字段
- 目的:为了满足新需求,需要更新 消息类型 而不破坏已有消息类型代码
即新、老版本需要兼容
- 更新字段时,需要符合下列规则:
2. 扩展消息对象 的字段
- 作用:使得其他人可以在自己的
.proto
文件中为 该消息对象 声明新的字段而不必去编辑原始文件- 注:扩展 可以是消息类型也可以是字段类型
- 以下以 扩展 消息类型 为例
- 注:扩展 可以是消息类型也可以是字段类型
A.proto
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
现在,其他人 就可以在自己的 .proto
文件中 添加新字段到Request
里。如下:
B.proto
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 要访问 扩展字段 的方法与 访问普通的字段 不同:使用专门的扩展访问函数
- 实例:
- 1
- 2
- 3
- 4
- 5
- 1
- 2
- 3
- 4
- 5
嵌套的扩展
可以在另一个 消息对象里 声明扩展,如:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 对于嵌套的使用,一般的做法是:在扩展的字段类型的范围内定义该扩展
- 实例:一个 Request 消息对象需要扩展(扩展的字段类型是Car 消息类型),那么,该扩展就定义在 Car消息类型 里:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 至此,
Protoco Buffer
的语法已经讲解完毕 - 关于如何根据需求 通过
Protoco Buffer
语法 去构建 数据结构 相信大家已经非常熟悉了。 - 在将
.proto
文件保存后,进入下一个步骤
步骤2:通过 Protocol Buffer
编译器 编译 .proto 文件
- 作用:将
.proto
文件 转换成 对应平台的代码文件Protoco Buffer
提供C++、Java、Python
三种开发语言的 API - 具体生成文件与平台有关:
- 编译指令说明
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 具体实例
- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
在指定的目录能看到一个Demo的包文件(含 java类
文件)
编译功能的拓展
a. 使用Android Studio
插件进行编译
- 需求场景:每次手动执行
Protocol Buffer
编译器将.proto
文件转换为Java
文件 操作不方便 - 解决方案:使用
Android Studio
的gradle
插件protobuf-gradle-plugin
,以便于在项目编译时 自动执行Protocol Buffers 编译器
关于protobuf-gradle-plugin
插件有兴趣的读者可自行了解,但个人还是建议使用 命令行,毕竟太过折腾插件没必要
b. 动态编译
- 需求场景:某些情况下,人们无法预先知道 .proto 文件,他们需要动态处理一些未知的 .proto 文件
如一个通用的消息转发中间件,它无法预先知道需要处理什么类型的数据结构消息
- 解决方案:动态编译
.proto
文件
由于使用得不多,此处不作过多描述,具体请看官方文档
c. 编写新的 .proto
编译器
- 需求场景:
Protocol Buffer
仅支持C++、java 和 Python
三种开发语言,一旦超出该三种开发语言,Protocol Buffer
将无法使用 - 解决方案:使用
Protocol Buffer
的Compiler
包 开发出支持其他语言的新的.proto
编译器
由于使用得不多,此处不作过多描述,具体请看官方文档
5.3 应用到具体平台(Android
平台)
- 终于到了应用到具体平台项目中的步骤了。
此处以
Android
平台 为例 - 具体步骤如下:
步骤1:将生成的 代码文件 放入到项目中
- 对于
Android(Java)平台
,即将编译.proto
文件生成的Java
包文件 整个复制到Android
项目中 - 放置路径:
app/src/main/java的
文件夹里
步骤2:在 Gradle
添加 Protocol Buffer
版本依赖
- 1
- 2
- 1
- 2
步骤3:具体在Android项目中使用
3.1 消息对象类介绍
通过.proto文件
转换的 Java
源代码 = Protocol Buffer
类 + 消息对象类(含Builder
内部类)
消息对象类 是
Protocol Buffer
类的内部类
由于最常用的都是 消息对象类 和其内部类Builder
类 的方法&成员变量,所以此处主要讲解这两者。
3.1.1 消息对象类(Message
类)
- 消息对象类 类通过 二进制数组 写 和 读 消息类型
- 使用方法包括:
- 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
- 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
常用的如上,更多请看官方文档
3.1.2 Builder
类
作用:创建 消息构造器 & 设置/ 获取消息对象的字段值 & 创建 消息类 实例
属于 消息对象类 的内部类
a. 创建 消息构造器
- 1
- 1
b. 设置/ 获取 消息对象的字段值 具体方法如下:
- 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
- 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
3.2 具体使用
使用步骤如下:
步骤1:通过 消息类的内部类Builder
类 构造 消息构造器
步骤2:通过 消息构造器 设置 消息字段的值
步骤3:通过 消息构造器 创建 消息类 对象
步骤4:序列化 / 反序列化 消息具体使用如下:(注释非常清晰)
- 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
- 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
Demo 地址
Carson_Ho的Github :https://github.com/Carson-Ho/ProtocolBuffer
高级功能
贴心的Google还提供将
Protocol Buff
编码方式 转化为 其他编码方式,如Json
、XML
等等即将
Protocol Buff
对象 转化为其他编码方式的数据存储对象下面展示的是 将
Protocol Buff
对象 转化为Json
对象
- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
6. 总结
- 在 传输数据量较大的需求场景下,
Protocol Buffer
比XML、Json
更小、更快、使用 & 维护更简单! - 下面用 一张图 总结在 Android平台中使用
Protocol Buffer
的整个步骤流程:
- Google出品的Protocol Buffer,别只会用Json和XML了
- Google 出品的 Protocol Buffer,别只会用 JSON 和 XML 了
- 快来看看Google出品的Protocol Buffer,别只会用Json和XML了
- [置顶] 快来看看Google出品的Protocol Buffer,别只会用Json和XML了
- XML和Google 数据结构protocol buffer 比较
- Google Protocol Buffer 的使用和原理
- Google Protocol Buffer 的使用和原理
- Google Protocol Buffer 的使用和原理
- Google Protocol Buffer 的使用和原理
- Google Protocol Buffer 的使用和原理
- Google Protocol Buffer 的使用和原理
- Google Protocol Buffer 的使用和原理
- Google Protocol Buffer 的使用和原理
- Google Protocol Buffer 的使用和原理
- Google Protocol Buffer 的使用和原理
- Google Protocol Buffer 的使用和原理
- Google Protocol Buffer 的使用和原理
- Google Protocol Buffer 的使用和原理
- Win7 64位注册表与32位注册表的区别
- http视频教程
- javascript 单选框如何全部选中 更新 状态
- JAVA Swing GroupLayout
- My97DatePicker日期控件使用方法
- Google出品的Protocol Buffer,别只会用Json和XML了
- linux下c语言编程实例
- GSON 详解
- mac os下使用 Docker安装oracle-xe-11g数据库
- 算法学习-(二)动态规划算法
- mac docker设置阿里云镜像
- Odoo8.0在CentOS7的安装步骤
- Eclipse neon 安装SVN插件
- Leetcode--17. Letter Combinations of a Phone Number