Java中的Google協議緩沖區

總覽

協議緩沖區是一種用于結構化數據的開源編碼機制。 它是由Google開發的,旨在實現語言/平臺中立且可擴展。 在本文中,我的目的是介紹Java平臺上下文中協議緩沖區的基本用法。

Protobuff比XML更快,更簡單,并且比JSON更緊湊。 當前,支持C ++,Java和Python。 但是,還有其他平臺(不是Google所支持的)作為開放源代碼項目–我嘗試了PHP實現,但由于它尚未完全開發,因此我停止使用它。 盡管如此,支持仍在繼續。 隨著Google宣布支持Google App Engine中的PHP,我相信他們會將其提升到一個新的水平。

基本上,您定義使用.proto規范文件來一次構造數據的方式。 這類似于描述軟件組件的IDL文件或規范語言。 協議緩沖區編譯器(protoc)使用此文件,該協議緩沖區編譯器將生成支持方法,以便您可以在各種流中讀寫對象。

消息格式非常簡單。 每種消息類型都有一個或多個唯一編號的字段(稍后我們將介紹原因)。 嵌套消息類型具有其自己的唯一編號字段集。 值類型可以是數字,布爾值,字符串,字節,集合和枚舉(受Java枚舉啟發)。 另外,您可以嵌套其他消息類型,從而使您可以按照與JSON允許的方式幾乎相同的方式分層結構化數據。

字段可以指定為可選必需重復 。 在Python中實現協議緩沖區時,不要讓字段的類型(例如enum,int32,float,string等)使您感到困惑。 在該領域的類型只是提示,protoc如何序列化的字段值,并產生你的郵件的郵件編碼格式(以后會更多)。 編碼格式看起來是對象的扁平化和壓縮表示形式。 無論您是在Python,Java還是C ++中使用協議緩沖區,都將以完全相同的方式編寫此規范。

Protobuff是可擴展的,您可以在以后的時間更新對象的結構,而不會破壞使用舊格式的程序。 如果要通過網絡發送數據,則可以使用Protocol Buffer API對數據進行編碼,然后序列化結果字符串。

可擴展性這一概念非常重要,因為Java以及與此相關的許多其他序列化機制可能會存在互操作性和向后兼容性的問題。 使用這種方法,您不必擔心在代碼中維護表示對象結構的serialVersionId字段。 維護該字段至關重要,因為Java的序列化機制將在反序列化對象時將其用作快速校驗和。 結果,一旦將對象序列化到某個文件系統或blob存儲中,以后就有可能對對象結構進行大刀闊斧的改變。 協議緩沖區受此影響較小。 只要您僅向對象添加可選字段,就可以反序列化舊類型,此時您可能會升級它們。

此外,您可以使用java_package關鍵字為.proto文件定義包名稱。 這樣可以很好地避免生成的代碼發生名稱沖突。 另一種選擇是像在下面的示例中一樣專門命名生成的類文件。 我在生成的類之前加上“ Proto”前綴,以表明這是一個生成的類。

這是一個簡單的消息規范,描述了帶有嵌入式地址消息User.proto的用戶:

option java_outer_classname="ProtoUser";message User {required int32  id = 1;  // DB record IDrequired string name = 2;required string firstname = 3;required string lastname = 4;required string ssn= 5; // Embedded Address message specmessage Address {required int32 id = 1;required string country = 2 [default = "US"];; optional string state = 3;optional string city = 4;optional string street = 5;optional string zip = 6;enum Type {HOME = 0;WORK = 1; }optional Type addrType = 7 [default = HOME]; }repeated Address addr = 16;
}

讓我們談談每個屬性右側看到的標簽號,因為它們非常重要。 這些標記在此規范的對象上以二進制表示形式標識消息的字段順序。 標記值1 – 15將被存儲為1個字節,而標記值16 – 2047的字段則需要2個字節進行編碼-不能確定為什么這樣做。 Google建議您將標簽1到15用于非常頻繁出現的數據,并在此范圍內保留一些標簽值以用于將來的更新。
注意:不能使用數字19000到19999。保留用于原型實現。 另外,您可以定義必填,重復和可選的字段。從Google文檔中:

  • required :格式正確的消息必須恰好具有此字段之一,即,嘗試使用未初始化的必填字段來構建消息會引發RuntimeException。
  • optional :格式正確的消息可以包含零個或一個此字段(但不能超過一個)。
  • repeated :在格式正確的消息中,此字段可以重復任意次(包括零次)。 重復值的順序將保留。

該文檔警告開發人員在使用required時要謹慎因為如果您決定棄用一個字段,則這種類型的字段會引起問題。 這是所有序列化機制都會遇到的經典向后兼容性問題。 Google工程師甚至建議對所有內容使用可選。

此外,我指定了一個嵌套消息規范地址。 我可以輕松地將此定義放置在同一原型文件中的User對象之外。 因此,對于相關的消息定義,將它們全部放在同一個.proto文件中是有意義的。 即使“地址”消息類型不是一個很好的例子,但是如果消息類型在其“父”對象之外不存在,我將使用嵌套類型。 例如,如果您要序列化LinkedListNode 。 那么在這種情況下,節點將是嵌入式消息定義。 這取決于您和您的設計。

可選的消息屬性被忽略時采用默認值。 特別是,使用特定于類型的默認值代替:對于字符串,默認值為空字符串;對于字符串,默認值為空字符串。 對于布爾值,默認值為false; 對于數字類型,默認值為零; 對于枚舉,默認值是枚舉類型定義中列出的第一個值(這很酷,但不太明顯)。

枚舉非常好。 它們跨平臺的工作方式與Java中的enum幾乎相同。 枚舉字段的值可以只是一個值。 您可以在消息定義內部或外部聲明枚舉,就好像它是自己的獨立實體一樣。 如果在消息類型內指定,則可以通過[Message-name]。[enum-name]公開另一種消息類型。

協議

針對.proto文件運行協議緩沖區編譯器時,編譯器將生成用于所選語言的代碼。 它將您的消息類型轉換為增強類,其中包括為屬性提供getter和setter等。 編譯器還生成方便的方法,以在輸出流和字符串之間來回串行化消息。

對于枚舉類型,生成的代碼將具有一個對應的Java或C ++枚舉,或者一個特殊的Python EnumDescriptor類,該類用于在運行時生成的類中創建帶有整數值的符號常量集。

對于Java,編譯器將為每種消息類型生成具有流利的Design Builder類的.java文件,以簡化對象的創建和初始化。 編譯器生成的消息類是不可變的。 一旦建立,便無法更改。

您可以在參考資料部分中閱讀有關其他平臺(Python,C ++)的信息,并在此處詳細介紹字段編碼:

https://developers.google.com/protocol-buffers/docs/reference/overview。

對于我們的示例,我們將使用–java_out命令行標志調用protoc。 該標志向編譯器指示生成的Java類的輸出目錄-每個原型文件一個Java類。

API

生成的API為以下便捷方法提供支持:

  • isInitialized()
  • toString()
  • mergeFrom(...)
  • 明確()

對于解析和序列化:

  • byte [] toByteArray()
  • parseFrom()
  • writeTo(OutputStream)在示例代碼中用于編碼
  • parseFrom(InputStream)在示例代碼中用于解碼

樣例代碼

讓我們建立一個簡單的項目。 我喜歡遵循Maven的默認原型:

protobuff-example / src / main / java / [應用程序代碼] protobuff-example / src / main / java / gen [生成的原型類] protobuff-example / src / main / proto [原型文件定義]

為了生成協議緩沖區類,我將執行以下命令:

#  protoc --proto_path=/home/user/workspace/eclipse/trunk/protobuff/--java_out=/home/user/workspace/eclipse/trunk/protobuff/src/main/java /home/user/workspace/eclipse/trunk/protobuff/src/main/proto/User.proto

我將展示一些生成的代碼,并簡要介紹它們。 生成的類很大,但是很容易理解。 它將提供構建器來創建用戶和地址的實例。

public final class ProtoUser {public interface UserOrBuilderextends com.google.protobuf.MessageOrBuilder...public interface AddressOrBuilderextends com.google.protobuf.MessageOrBuilder {....}

生成的類包含用于真正流暢地創建對象的Builder接口。 這些構建器接口在原型文件中指定的每個屬性都有getter和setter,例如:

public String getCountry() {java.lang.Object ref = country_;if (ref instanceof String) {return (String) ref;} else {com.google.protobuf.ByteString bs =(com.google.protobuf.ByteString) ref;String s = bs.toStringUtf8();if (com.google.protobuf.Internal.isValidUtf8(bs)) {country_ = s;}return s;}}

由于這是一種自定義編碼機制,因此邏輯上所有字段都具有自定義字節包裝器。 我們的簡單String字段在存儲時使用ByteString進行壓縮,然后將其反序列化為UTF-8字符串。

// required int32 id = 1;public static final int ID_FIELD_NUMBER = 1;private int id_;public boolean hasId() {return ((bitField0_ & 0x00000001) == 0x00000001);}

在這次電話會議中,我們看到了開頭提到的標簽號的重要性。 這些標簽號似乎代表某種位位置,這些位位置定義了數據在字節串中的位置。 接下來,我們看一下前面提到的write和read方法的代碼片段。

將實例寫入輸出流:

public void writeTo(com.google.protobuf.CodedOutputStream output)throws java.io.IOException {getSerializedSize();if (((bitField0_ & 0x00000001) == 0x00000001)) {output.writeInt32(1, id_);}if (((bitField0_ & 0x00000002) == 0x00000002)) {output.writeBytes(2, getCountryBytes());
....
}

從輸入流中讀取:

public static ProtoUser.User parseFrom(java.io.InputStream input)throws java.io.IOException {return newBuilder().mergeFrom(input).buildParsed();
}

此類約為2000行代碼。 還有其他詳細信息,例如如何映射Enum類型以及如何存儲重復的類型。 希望我提供的代碼片段可以使您對該類的結構有一個較高的了解。

讓我們看一些使用生成的類的應用程序級代碼。 要保留數據,我們可以簡單地執行以下操作:

// Create instance of AddressAddress addr = ProtoUser.User.Address.newBuilder()  .setAddrType(Address.Type.HOME)        .setCity("Weston").setCountry("USA").setId(1).setState("FL").setStreet("123 Lakeshore").setZip("90210").build();// Serialize instance of UserUser user = ProtoUser.User.newBuilder() .setId(1).setFirstname("Luis").setLastname("Atencio").setName("luisat").setSsn("555-555-5555")          .addAddr(addr).build();// Write fileFileOutputStream output = new FileOutputStream("target/user.ser");  user.writeTo(output);          output.close();

一旦堅持下來,我們可以這樣讀:

User user = User.parseFrom(new FileInputStream("target/user.ser");System.out.println(user);

要運行示例代碼,請使用:

java -cp。:../ lib / protobuf-java-2.4.1.jar app.Serialize ../target/user.ser

Protobuff與XML

Google聲稱協議緩沖區比XML快20到100倍(以納秒為單位),而刪除空白則小3到10倍。 但是,直到所有平臺(不僅是上述3種平臺)都得到支持和采用,XML仍將繼續成為非常流行的序列化機制。 此外,并非每個人都具有Google用戶對性能的要求和期望。 XML的替代方法是JSON。

Protobuff與JSON

我進行了一些比較測試,以評估在JSON上使用協議緩沖區的性能。 結果令人震驚,一個簡單的測試顯示,就存儲而言,原型增益器的效率提高了50%以上。 我創建了一個簡單的POJO版本的User-Address類,并使用GSON庫對一個實例進行了編碼,該實例的狀態與上述示例相同(我將省略實現細節,請檢查下面引用的gson項目)。 編碼相同的用戶數據,我得到:

-rw-rw-r-- 1 luisat luisat 206 May 30 09:47 json-user.ser 
-rw-rw-r-- 1 luisat luisat 85 May 30 09:42  user.ser

這很了不起。 我也在另一個博客中找到了它(請參閱下面的資源):

絕對值得一讀。

結論和進一步說明

協議緩沖區可能是跨平臺數據編碼的良好解決方案。 使用Java,Python,C ++和其他許多語言編寫的客戶端,存儲/發送壓縮數據非常簡單。

一個棘手的觀點是:“永遠記住需要的信息。” 如果您發瘋了,并且需要.proto文件的每個字段,那么刪除或編輯這些字段將非常困難。

同樣有一點激勵作用,即在Google的數據存儲中使用probbuff 在Google的代碼樹中,跨12,183個.proto文件定義了48,162種不同的消息類型。

協議緩沖區促進了良好的面向對象設計,因為.proto文件基本上是愚蠢的數據持有者(如C ++中的結構)。 根據Google文檔,如果您想向生成的類添加更豐富的行為,或者您無法控制.proto文件的設計,則最好的方法是將生成的協議緩沖區類包裝在應用程序中,具體類別。

最后,請記住,永遠不要通過從生成的類繼承行為來向它們添加行為。 這將破壞內部機制,無論如何都不是一個好的面向對象的實踐。

這里介紹的許多信息來自個人經驗,其他資源,最重要的是來自Google開發人員代碼。 請在參考資料部分中查閱文檔。

資源資源

  1. https://developers.google.com/protocol-buffers/docs/overview
  2. https://developers.google.com/protocol-buffers/docs/proto
  3. https://developers.google.com/protocol-buffers/docs/reference/java-generated
  4. https://developers.google.com/protocol-buffers/docs/reference/overview
  5. http://code.google.com/p/google-gson/
  6. http://afrozahmad.hubpages.com/hub/protocolbuffers

參考:我們的JCG合作伙伴 Luis Atencio的Java協議緩沖區 ,在Reflective Thought博客上。


翻譯自: https://www.javacodegeeks.com/2012/06/google-protocol-buffers-in-java.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/372326.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/372326.shtml
英文地址,請注明出處:http://en.pswp.cn/news/372326.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

匈牙利哦模板 二分匹配 完全匹配問題

匈牙利算法的核心思想就是 騰空間, 有條件 創造,沒條件也要創造! bool find(int x){int i,j;for (j1;j<m;j){ //掃描每個被匹配的人 if (line[x][j]true && used[j]false) //如果有關系并且還沒有標記過(這里標記的意思是這次查找曾試圖改變過的歸屬問題&a…

ThinkPHP 中驗證碼的看不清切換

<!--HTML頁面--> <!DOCTYPE html><html><head> <title></title></head><body><script type"text/javascript" src"__PUBLIC__/js/jquery-1.8.2.min.js"></script><form action"{:U(H…

mysql從表截取信息_mysql中循環截取用戶信息并插入到目標表對應的字段中

操作環境&#xff1a;有表game_list&#xff0c;字段&#xff1a;uid&#xff0c;score1&#xff0c;score2&#xff0c;seat_id&#xff0c;last_update&#xff1b;傳入參數為i_player_detail &#xff0c;傳入的值為多個用戶的id、之前分數、之后分數、座位號&#xff0c;每…

Java中的數組,列表,集合,映射,元組,記錄文字

有時&#xff0c;當我對JavaScript的強大功能和表現力感到興奮時&#xff0c;我發現自己錯過了Java世界中的一兩個功能。 除了lambda表達式/閉包或任何您想稱為“匿名函數”的東西之外&#xff0c;它還對數組&#xff0c;數組&#xff0c;列表&#xff0c;集合&#xff0c;映射…

mysql鎖表問題的解決方法_MYSQL鎖表問題的解決方法

本文實例講述了MYSQL鎖表問題的解決方法。分享給大家供大家參考&#xff0c;具體如下&#xff1a;很多時候&#xff01;一不小心就鎖表&#xff01;這里講解決鎖表終極方法&#xff01;案例一mysql>show processlist;參看sql語句一般少的話mysql>kill thread_id;就可以解…

linux——(1)初識linux

linux有窗口管理員環境和純文本界面環境&#xff0c;同時linux默認提供6個Terminal來讓用戶登錄。crtlaltF1-6可自由切換。其中如果窗口管理員環境處于運行狀態&#xff0c;那么可以按crtlaltF7直接切過去。 常用命令&#xff1a; cd [dir] #進入dir目錄下 ls #列出當前目錄下的…

4.26學習成果

哇&#xff0c;今天終于開始接觸Web了&#xff0c;感覺有點小興奮&#xff0c;這幾天看來那個視頻感覺挺有趣的&#xff0c;挺奇妙的。看到人家敲代碼&#xff0c;感覺好厲害。但是感覺不懂&#xff0c;所以&#xff0c;要努力學習了。 今天的學習成果&#xff1a; 網頁由什么組…

將Glassfish 3連接到外部ActiveMQ 5代理

介紹 在ONVZ&#xff0c;我們將Glassfish 3用作開發和生產應用服務器&#xff0c;我們對其性能和穩定性以及周圍的廣大社區感到非常滿意。 我很少遇到在stackoverflow或java.net上沒有匹配解決方案的問題。 作為我們開源策略的一部分&#xff0c;我們還運行了一個定制的ActiveM…

esp8266 lcd 天氣_ESP8266 顯示實時天氣信息

代碼文件getdata.h#include #include #include #include #include #include #include #define DEBUG 1#define MAX_CONTENT_SIZE 2000const char* ssid "weather";const char* password "mymymymy";WiFiClient client;HTTPClient http;char response[MAX…

【VS開發】visual studio 2015的NuGet Manager解決方案管理功能

NuGet的官方說明是&#xff1a;NuGet是一款Visual Studio的擴展&#xff0c;它可以簡單的安裝、升級開源庫和工具。 官網地址&#xff1a;http://www.nuget.org/ 官網最醒目的位置就是下載鏈接&#xff0c;安裝完成后我們來快速體驗一把。 手上有個小項目需要使用到json格式&am…

五. 面向對象高級特性4. 接口的概念和使用

在抽象類中&#xff0c;可以包含一個或多個抽象方法&#xff1b;但在接口(interface)中&#xff0c;所有的方法必須都是抽象的&#xff0c;不能有方法體&#xff0c;它比抽象類更加“抽象”。接口使用 interface 關鍵字來聲明&#xff0c;可以看做是一種特殊的抽象類&#xff0…

智能配料

我們都有多少次聽說“分批處理”會增加延遲&#xff1f; 作為對低延遲系統充滿熱情的人&#xff0c;這讓我感到驚訝。 以我的經驗&#xff0c;正確完成批處理不僅可以提高吞吐量&#xff0c;還可以減少平均延遲并保持一致。 那么&#xff0c;批處理如何神奇地減少延遲呢&#x…

mysql從myisam_將MySQL從MyISAM轉換成InnoDB錯誤和解決辦法

原來自己用的是為了裝的&#xff0c; 所以在設置database usage(如下圖1)的時候按照discuz官方的建議&#xff0c;選的都是Non-Transactional Database Only(只支持MyISAM數據引擎的非事務數據庫)&#xff0c;用MyISAM數據庫&#xff0c;還沒涉及到需要InnoDB&#xff0c;因此打…

相似性度量中用到的一些距離函數

本文目錄 1. 歐氏距離 2. 曼哈頓距離 3. 切比雪夫距離 4. 閔可夫斯基距離 5. 標準化歐氏距離 6. 馬氏距離 7. 漢明距離 8. 杰卡德距離 & 杰卡德相似系數 9. 相關系數 & 相關距離 10. 信息熵 1. 歐氏距離(Euclidean Distance) 歐氏距離是最易于理解的一種距離計算方法&a…

Spring 3.1配置文件和Tomcat配置

Spring 3.1引入了非常有用的功能&#xff0c;稱為配置文件 。 因此&#xff0c;它易于構建&#xff0c;可以在所有環境&#xff08;開發&#xff0c;測試&#xff0c;生產等&#xff09;中部署的軟件包。 通過定義系統屬性spring.profiles.active&#xff0c; Spring允許我們使…

計算1~n之間所有奇數之和_所有奇數長度子數組的和

所有奇數長度子數組的和題目&#xff1a;給你一個正整數數組 arr &#xff0c;請你計算所有可能的奇數長度子數組的和。子數組 定義為原數組中的一個連續子序列。請你返回 arr 中 所有奇數長度子數組的和 。示例 1&#xff1a;輸入&#xff1a;arr [1,4,2,5,3]輸出&#xff1a…

MYSQL AND OR的聯用

MYSQL AND OR的聯用 MYSQL中”AND”和”OR”都是條件控制符。”AND”是求交集&#xff0c;而”OR”則是求并集&#xff0c;非常多情況下&#xff0c;須要聯用它們兩個。下面是兩張表,我僅僅列出實用的字段。 Table:student_score 學生成績 sid(學生ID) cid(課程ID) score(分數)…

九度oj 題目1456:勝利大逃亡

題目描述&#xff1a;Ignatius被魔王抓走了,有一天魔王出差去了,這可是Ignatius逃亡的好機會.魔王住在一個城堡里,城堡是一個A*B*C的立方體,可以被表示成A個B*C的矩陣,剛開始Ignatius被關在(0,0,0)的位置,離開城堡的門在(A-1,B-1,C-1)的位置,現在知道魔王將在T分鐘后回到城堡,I…

JMX:一些入門說明

JMX&#xff08;Java管理擴展&#xff09;是一種J2SE技術&#xff0c;可以管理和監視Java應用程序。 基本思想是實現一組管理對象&#xff0c;并將實現注冊到平臺服務器&#xff0c;在平臺服務器上&#xff0c;可以使用一組連接器或適配器從本地或遠程調用這些實現到JVM。 一個…

解釋java程序中的異常機制_Java編程中的異常機制

本文旨在以初學者的角度來學習Java異常的知識&#xff0c;盡量簡單&#xff0c;一些細枝末節的知識不會講述&#xff0c;但不影響對知識的掌握。&#xff08;比如try-catch可以嵌套&#xff0c;不太會這么用&#xff09;1.什么是異常我們先舉個例子int x 10/0;在IDE里輸入這樣…