1.下载安装:
google protocol buffer 的官网地址是:http://code.google.com/p/protobuf/
建议下载稳定版本:protobuf-2.4.1 linux下载protobuf-2.4.1.tar.bz2 windows下载protobuf-2.4.1.zip
这里以linux下安装为实例:
tar -xvf protobuf-2.4.1.tar.bz2
cd protobuf-2.4.1
./configure --prefix=/usr/local/protobuf-2.4.1
make
make install
2.使用protobuf
查看编译生成的目录
cd /usr/local/protobuf-2.4.1
ls
bin include lib
其中,bin中的protoc是.proto文件的处理器,可用这个工具生成cpp,java,python文件.
由于系统常用这个工具,可以将其ln或者直接拷贝到系统环境bin下
ln -s /usr/local/protobuf-2.4.1/bin/protoc /usr/bin/protoc
同样,可以将头文件ln或者直接拷贝到系统环境
ln -s /usr/local/protobuf-2.4.1/include/google /usr/include/google
将lib文件ln或者直接拷贝到系统环境
略,方法同上.
这个时候,protobuf的开发环境已经搭建了.
3.如何使用protobuf
- 数据结构体:
- message message_name{message_body;}
- message_body格式:
- 例如required int32 query = 1[defaut=10];
- 形式为:rule type name = value[other_rule];
- 规则:
- required表示必须具有该值域;
- optional表示可选的值域;
- repeated表示可重复的值域(即>=0);
- 其中requered/optional是常用rule,而repeated则不常用同时因为是历史遗留现使用repeated int32 samples=4[packed=true];形式;
-
- value值:
- value值最小为1,是底层编码时使用其中1-15占一位,>15则会占多位;
- 不同的message中的value值互不干扰,常以1开始计数。
-
- 数据类型之基本类型:
- .proto Type C++ Type Java Type
- double double double
- float float float
- int32 int32 int
- int64 int64 long
- uint32 uint32 int
- uint64 uint64 long
- sint32 int32 int
- sint64 int64 long
- fixed32 uint32 int
- fixed64 uint64 long
- sfixed32 int32 int
- sfixed64 int64 long
- bool bool boolean
- string string String
- bytes string ByteString
-
- 数据类型之复杂类型:
- 复杂类型主要包括:枚举,其他message,groups等。
- 枚举定义例如:enum Corpus{WEB=0;LOCAL=1}
- 枚举定义在message中。
- 可以使用其他message作为类型来定义成员。
- groups我的理解有些像C++中的union结构。
-
- 嵌套定义:
- 可以嵌套定义message结构,而嵌套定义的message被其他message作为成员类型时需要形式为outmessage.inmessage形式。
-
- 包结构:
- 定义形式:package foo.bar;
- 对应C++中则生成两个命名空间foo和bar,且bar定义在foo中;
- 可以通过import "myproject/other_protos.proto";来引入.proto文件;
- 引用其他package中message时需要完整的package路径;
-
- Services:
- 主要用于RPC系统中,在.proto中定义接口;
- 定义形式如例子:
- service SearchService {
- rpc Search(SearchRequest) return (SearchResponse);
- }
-
- .proto文件编译:
- 格式:
- protoc -–proto_path=(.proto文件路径) -–cpp_out=(.cc .java生成文件路径) (.proto文件路径)/?.proto
- -–proto_path 简化为: --I
- 其中可根据需要更改:cpp_out选项为java_out/python_out。
- 例子:
- protoc -I=./ --cpp_out=./ model.proto
我们拿个例子:
建立model.proto
- package cn.vicky.model.seri;
-
- message User {
- required int32 id = 1; // 主键,唯一
- required string username = 2; // 帐号
- required string password = 3; // 密码
- optional string email = 4; // 邮箱(可选)
- repeated Person person = 5; // 账户拥有的角色(可以重复)
- }
-
- message Person {
- required int32 id = 1; // 主键,唯一
- required string name = 2; // 角色名字
-
- repeated PhoneNumber phone = 3; // 电话号码(可以重复)
- }
-
- // 枚举类型
- enum PhoneType {
- MOBILE = 0;
- HOME = 1;
- WORK = 2;
- }
-
- message PhoneNumber {
- required string number = 1;
- optional PhoneType type = 2 [default = HOME];
- }
protoc -I=./ --cpp_out=./ model.proto
将生成对应的model.pb.h model.pb.cc
使用:
编写main.cpp
- /*
- * File: main.cpp
- * Author: Vicky.H
- * Email: [email protected]
- */
- #include <iostream>
- #include <fstream>
- #include "model.pb.h"
-
- /*
- *
- */
- int main(void) {
-
- // 创建User对象
- cn::vicky::model::seri::User u;
- u.set_id(1);
- u.set_username("Jack");
- u.set_password("123456");
- u.set_email("[email protected]");
-
- // 创建User中的一个角色
- cn::vicky::model::seri::Person* _person1 = u.add_person();
- _person1->set_id(1);
- _person1->set_name("P1");
-
- // 创建角色中的一个电话号码:1
- cn::vicky::model::seri::PhoneNumber* _phone1 = _person1->add_phone();
- _phone1->set_number("+8613618074943");
- _phone1->set_type(cn::vicky::model::seri::MOBILE);
-
- // 创建角色中的一个电话号码:2
- cn::vicky::model::seri::PhoneNumber* _phone2 = _person1->add_phone();
- _phone2->set_number("02882334717");
- _phone2->set_type(cn::vicky::model::seri::WORK);
-
-
- // 创建User中的一个角色
- cn::vicky::model::seri::Person* _person2 = u.add_person();
- _person2->set_id(2);
- _person2->set_name("P2");
-
- // 创建角色中的一个电话号码:1
- cn::vicky::model::seri::PhoneNumber* _phone3 = _person2->add_phone();
- _phone3->set_number("+8613996398667");
- _phone3->set_type(cn::vicky::model::seri::MOBILE);
-
- // 创建角色中的一个电话号码:2
- cn::vicky::model::seri::PhoneNumber* _phone4 = _person2->add_phone();
- _phone4->set_number("02882334717");
- _phone4->set_type(cn::vicky::model::seri::WORK);
-
-
- // 持久化:
- // std::fstream out("User.pb", std::ios::out | std::ios::binary | std::ios::trunc);
- // u.SerializeToOstream(&out);
- // out.close();
-
- // 对象化:
- cn::vicky::model::seri::User u2;
- std::fstream in("User.pb", std::ios::in | std::ios::binary);
- if (!u2.ParseFromIstream(&in)) {
- std::cerr << "Failed to parse User.pb." << std::endl;
- exit(1);
- }
-
- std::cout << u2.id() << std::endl;
- std::cout << u2.username() << std::endl;
- std::cout << u2.password() << std::endl;
- std::cout << u2.email() << std::endl;
-
- std::cout << "---------------------------" << std::endl;
- for(int i = 0;i < u2.person_size();i++) {
- cn::vicky::model::seri::Person* p = u2.mutable_person(i);
- std::cout << p->id() << std::endl;
- std::cout << p->name() << std::endl;
- for (int j = 0;j < p->phone_size();j++) {
- cn::vicky::model::seri::PhoneNumber* phone = p->mutable_phone(j);
- std::cout << phone->number() << std::endl;
- }
- std::cout << "---------------------------" << std::endl;
- }
- return 0;
- }
需要 -lpthread -lprotobuf (protobuf已经被加载到了/usr/lib)
执行后,会生成:User.pb,存储的二进制文件.可以直接打开看看.
以上,我们使用了protobuf完成c++下的对象序列化以及反序列化.这里我们要描述一下protobuf的优势了.
那就是protobuf性能高效,他的序列化速度比java自身的序列化还快数倍,而且支持3种语言对象的转换.以往,在C++中序列化的对象,比如用boost serialization持久化的对象,无法用java展开,即便使用jni技术,这也是非常麻烦的事.现在我们有protobuf了.
运行: protoc -I=./ --java_out=./ model.proto 将生成对应的Java类
我们可以用Maven建立一个Java工程.需要protobuf的java依赖库:
- <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
- <modelVersion>4.0.0</modelVersion>
-
- <groupId>cn.vicky</groupId>
- <artifactId>google_protobuf_01_java</artifactId>
- <version>1.0-SNAPSHOT</version>
- <packaging>jar</packaging>
-
- <name>google_protobuf_01_java</name>
- <url>http://maven.apache.org</url>
-
- <properties>
- <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
- </properties>
-
- <dependencies>
- <dependency>
- <groupId>com.google.protobuf</groupId>
- <artifactId>protobuf-java</artifactId>
- <version>2.4.1</version>
- </dependency>
- </dependencies>
- </project>
编写Test.java
- package cn.vicky.model.seri;
-
- import java.io.File;
- import java.io.FileInputStream;
- import java.io.FileNotFoundException;
- import java.io.IOException;
- import java.io.InputStream;
-
- /**
- *
- * @author Vicky.H
- */
- public class Test {
-
- public static void main(String args[]) throws FileNotFoundException, IOException {
- File file = new File("User.pb");
- InputStream is = new FileInputStream(file);
- Model.User user = Model.User.parseFrom(is);
- System.out.println(user.getId());
- System.out.println(user.getUsername());
- System.out.println(user.getPassword());
- System.out.println(user.getEmail());
- System.out.println("-------------------");
- for (Model.Person person : user.getPersonList()) {
- System.out.println(person.getId());
- System.out.println(person.getName());
- for (Model.PhoneNumber phone : person.getPhoneList()) {
- System.out.println(phone.getNumber());
- }
- System.out.println("-------------------");
- }
- is.close();
- }
- }
运行:
1
Jack
123456
[email protected]
---------------------------
1
P1
+8613618074943
02882334717
---------------------------
2
P2
+8613996398667
02882334717
---------------------------
运行 SUCCESSFUL (总时间: 594ms)
OK.以上我们完成了probuf在C++,Java的使用.非常强力是不是!!
设计思想:
在POJO中,protobuf生成的类,处于PO状态,而且这个生成的类,我们最好不要做任何修改或太大的修改,那么,这个时候,我们可以通过C++友元类的方式,为PO添加一个JO类.将数据结构算法分离,也就是说,PO是数据,JO放算法!!!
与数据库的结合:
mysql oracle 可以很轻松的存储,读取二进制.还有一点,那就是通过这种方式,我们可以非常简单的将C++的对象,持久化的redis之类内存数据库了.
附:
model.proto也可以这样定义,不过,本人认为,上面的更好,这里仅供参考,采用什么样的方式,生成的类的结构也不太一样.
- package cn.vicky.model.seri;
-
- message User {
- required int32 id = 1; // 主键,唯一
- required string username = 2; // 帐号
- required string password = 3; // 密码
- optional string email = 4; // 邮箱(可选)
-
- message Person {
- required int32 id = 1; // 主键,唯一
- required string name = 2; // 角色名字
-
- // 枚举类型
- enum PhoneType {
- MOBILE = 0;
- HOME = 1;
- WORK = 2;
- }
-
- message PhoneNumber {
- required string number = 1;
- optional PhoneType type = 2 [default = HOME];
- }
-
- repeated PhoneNumber phone = 3; // 电话号码(可以重复)
- }
-
- repeated Person person = 5; // 账户拥有的角色(可以重复)
- }