protobuf那些事


大家好,俺又來寫博客了.......上次劇情預告說,這次會寫hive的博客.......好吧,那俺就不打算寫hive了.......老碼農路子就是要野(本人不老,不能說得影響了找女票)......這次咱們玩什么呢,我之前就看重了一個比較好玩的小玩意兒,那就是來自google的技術,protobuf.

上次的博客我看了之后很失望啊,閱讀數並不高....我在想是不是大家對hadoop之類的並不感興趣,所以就先換換口味吧.Google Protocol Buffer( 簡稱 Protobuf) 是 Google 公司內部的混合語言數據標准,目前已經正在使用的有超過 48,162 種報文格式定義和超過 12,183 個 .proto 文件。他們用於 RPC 系統和持續數據存儲系統。Protocol Buffers 是一種輕便高效的結構化數據存儲格式,可以用於結構化數據串行化,或者說序列化。它很適合做數據存儲或 RPC 數據交換格式。可用於通訊協議、數據存儲等領域的語言無關、平台無關、可擴展的序列化結構數據格式。目前提供了 C++、Java、Python 三種語言的 API。

ok,這就是protubuf的一些比較抽象的介紹,不過簡單地說,protobuf其實就是一種數據傳輸的格式,事實上,我們需要知道pb這種格式用來傳輸數據到底會給我們帶來如何的好處.我們可以拿pb格式和json做對比.其實json這種數據傳輸的格式大家應該非常熟悉.但是json有什么不好的呢,首先json大,json格式是字符串形式,和壓縮為二進制的pb格式肯定是要來的大的.此外,json很坑,因為需要對不同的語言編寫json解析的程序.......php和python自然可以比較輕松的解析,java就坑了.......而且這些解析器的性能並一致,有好有壞.pb因為是大廠的技術嘛,人家搞一個編譯器,編譯為c++,java,python的解析代碼,不就搞定了.......好吧,大廠技術就是雄厚.......我等也只能仰望.....(老子要是有時間,也去寫一個)....

廢話不多說,直接搞起來.首先,你要去下載pb的tar包.至於安裝包在哪里,少年郎,去google上找吧........我裝的是2.6.1版,安裝非常簡單.

1 tar zxvf protobuf-2.6.1.tar.gz
2  cd protobuf-2.6.1
3 ./configure
4 make
5 make check
6 make install

這個編譯的過程依舊是無比的漫長.......很懷念大一的時候,寫個hello world,一秒不到就編譯完成,然后很快就能輸出結果的那種快樂的時光.........人的一生就是在等待中度過,等待一個可以白頭到老的人,等待一份可以吃飽飯的工作,等待有一天,能夠成為想成為的那個人.......其實等待是因為有希望,沒有希望,等待似乎就沒有意義了......所以,我一直在想,女票是會有的,飯也可以吃飽的,人不能沒有希望........

ok,我們安裝完了,來看看有沒有安裝上吧......

1 protoc --version

返回:libprotoc 2.6.1. 這樣,就安裝成功了.

然后我們怎么用這個工具呢?

  1. 首先,我們要做的是,定義一套.proto格式的消息的定義文件.我們在具體的使用中來慢慢來港如何書寫這個proto綴的文件.
1 message student{
2     required int64 sid = 1;//學生id
3     required string name = 2;//學生姓名
4 }

  這里面我們發現:

  • message類似於C里面的struct,表示我們定義的是一個消息類型.
  • studeng是具體的類型名稱.
  • required是表示這個字段是必須賦值的.
  • int64和string是該字段的類型.
  • =后面的數字表示是該字段在二進制文件中的序號,name為2,表示它一定在sid的后面.

  現在我們如果想要加上一個性別的字段和年齡的字段,性別的字段是enum類型.年齡類型是int32,怎喵加呢?很簡單.

 1 enum Sex{
 2     MALE = 1;
 3     FEMALE = 2;
 4 }
 5 
 6 message Student{
 7     required int64 sid = 1;//學生id
 8     required string name = 2;//學生姓名
 9     optional Sex sex = 3; //學生性別
10     optional int32 age = 4;//學生年紀
11 }

  easy啊.等等,現在我們又有一個消息,比如我們有個班級的消息,希望能夠列出里面每個學生的這些基本信息,怎么搞呢?

  首先,我們需要定義一個班級的消息.

1 message Banji{
2     required int64 cid = 1;//班級的id
3     required string cname = 2;//班級的編號
4     repeated Student students = 3;//學生
5 }

  這里我們要注意幾個點:

  • required前綴表示該字段為必要字段,既在序列化和反序列化之前該字段必須已經被賦值。與此同時,在Protocol Buffer中還存在另外兩個類似的關鍵字,optional和repeated,帶有這兩種限定符的消息字段則沒有required字段這樣的限制。
  • 標簽值為1到15的字段在編碼時可以得到優化,既標簽值和類型信息僅占有一個byte.標簽范圍是16到2047的將占有兩個bytes,而ProtocolBuffer可以支持的字段數量則為2的29次方減一。有鑒於此,我們在設計消息結構時,可以盡可能考慮讓repeated類型的字段標簽位於1到15之間,這樣便可以有效的節省編碼后的字節數量。
  • 在每個消息中必須至少留有一個required類型的字段.
  • 如果打算在原有消息協議中添加新的字段,同時還要保證老版本的程序能夠正常讀取或寫入,那么對於新添加的字段必須是optional或repeated。道理非常簡單,老版本程序無法讀取或寫入新增的required限定符的字段。
  • 在原有的消息中,不能移除已經存在的required字段,optional和repeated類型的字段可以被移除,但是他們之前使用的標簽號必須被保留,不能被新的字段重用。
  • int32、uint32、int64、uint64和bool等類型之間是兼容的,sint32和sint64是兼容的,string和bytes是兼容的,fixed32和sfixed32,以及fixed64和sfixed64之間是兼容的,這意味着如果想修改原有字段的類型時,為了保證兼容性,只能將其修改為與其原有類型兼容的類型,否則就將打破新老消息格式的兼容性。

  這里還有一個問題,Banji這個消息要用到Student的定義,我們可以回憶一下java中的處理方式,這種互相定義之間的依賴可以用package和import來解決.這樣,我們可以給出完整的.proto的定義方式:

  test.student.proto

package test;
option java_package = "com.songfy.pb";
option java_outer_classname = "StudentProtobuf";
enum Sex{
        MALE = 1;
        FEMALE = 2;
}

message Student{
        required int64 sid = 1;//學生id
        required string name = 2;//學生姓名
        optional Sex sex = 3; //學生性別
        optional int32 age = 4;//學生年紀
}

  test.banji.proto

1 import "test.student.proto";
2 package test;
3 option java_package = "com.songfy.pb";
4 option java_outer_classname = "BanjiProtobuf";
5 message Banji{
6         required int64 cid = 1;//班級的id
7         required string cname = 2;//班級的編號
8         repeated Student students = 3;//學生
9 }

ok,這樣我們就把我們的消息類型定義好了....那么如何寫代碼呢?下面我們細細道來.

  2.編譯.proto

      我們這里用java來做示例,C++也成,但是還是java稍微方便一下,大家可以用C++試一試,python呢就算了,俺不太會python,也沒有興趣去學,個人喜好,勿噴,嗯,就這樣.ok,那現在我們就來編譯這個玩意兒.

1 protoc --java_out="/usr/home/feng/protos/src" test.banji.proto test.student.proto 

  吊炸天,瞬間產生了1000多行代碼,來自google的技術,四國一!

  不過現在問題來了,這代碼怎么用?這代碼看都看不懂,怎么用呢?不要驚慌,一定不要驚慌.......先靠到你的elipse再說....然后你就會發現一大堆的錯誤......嗯........

  這個解決的方法很簡單,將protobuf-java-2.6.1.jar下載下來,加入到我們的path中,那我們的問題就都解決了.

  接下來,我們來看看如何寫代碼.嗯,終於到了寫代碼的時候了........

  

 1 package com.songfy.pb;
 2 
 3 import java.io.FileInputStream;
 4 import java.io.FileOutputStream;
 5 import java.io.IOException;
 6 import java.util.ArrayList;
 7 import java.util.List;
 8 
 9 import com.songfy.pb.BanjiProtobuf.Banji;
10 import com.songfy.pb.StudentProtobuf.Sex;
11 import com.songfy.pb.StudentProtobuf.Student;
12 
13 public class Main {
14 
15     public static void main(String[] args) throws IOException {
16         
17         //獲取student的build sb
18         Student.Builder sb = Student.newBuilder();
19         //加入兩個學生
20         sb.setSid(110);
21         sb.setName("shuaiguo");
22         sb.setSex(Sex.MALE);
23         sb.setAge(11);
24         
25         List<Student> list = new ArrayList<Student>();
26         list.add(sb.build());
27         
28         sb = Student.newBuilder();
29         sb.setSid(119);
30         sb.setName("lilei");
31         sb.setSex(Sex.FEMALE);
32         sb.setAge(20);
33         
34         list.add(sb.build());
35         
36         //產生一個班級
37         Banji.Builder bb = Banji.newBuilder();
38         bb.setCid(13);
39         bb.setCname("dafengqi");
40         bb.addAllStudents(list);
41         
42         //刷入到文件中
43          FileOutputStream fos = new FileOutputStream("C:/Users/songfy/Desktop/test.protoout");
44          bb.build().writeTo(fos);
45          fos.close();
46          
47          //從文件讀回
48          Banji bb1 = Banji.parseFrom(new FileInputStream("C:/Users/songfy/Desktop/test.protoout"));
49          System.out.println(bb1.getCid() + "\t" + bb1.getCname());
50          for(Student s: bb1.getStudentsList()){
51              System.out.println(s.getSid() + "\t" + s.getName() + "\t" + s.getSex() + "\t" + s.getAge());
52          }
53 
54     }
55 
56 }

這就是我們代碼了,似乎非常easy啊........

這樣,我們可以看到輸出結果為:

13 dafengqi
110 shuaiguo MALE 11
119 lilei FEMALE 20

 

這正是我們想要的,這次的博客時間就到此為止了,我們下次再見........2333333......2333333.......

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM