1. Protobuf語法介紹
我們先看看官方文檔給出的定義和描述
簡單來講, ProtoBuf 是結構數據序列化方法,可簡單類比於,其具有以下特點:
在protobuf中,協議是由一系列的消息組成的。因此最重要的就是定義通信時使用到的消息格式。一個Protobuf 消息(對應java類),由至少一個欄位(對應Java類屬性)組合而成。
消息的定義很簡單,就是message關鍵字加上消息的名字
欄位定義格式:
限定修飾符 | 數據類型 | 欄位名稱 | = | 欄位編碼
required:
表示是一個必須欄位,必須相對於發送方,在發送消息之前必須設置該欄位的值,對於接收方,必須能夠識別該欄位的意思。發送之前沒有設置required欄位或者無法識別required欄位都會引發編解碼異常,導致消息被丟棄。
optional:
表示是一個可選欄位,可選對於發送方,在發送消息時,可以有選擇性的設置或者不設置該欄位的值。對於接收方,如果能夠識別可選欄位就進行相應的處理,如果無法識別,則忽略該欄位,消息中的其它欄位正常處理。---因為optional欄位的特性,很多介面在升級版本中都把後來添加的欄位都統一的設置為optional欄位,這樣老的版本無需升級程序也可以正常的與新的軟體進行通信,只不過新的欄位無法識別而已,因為並不是每個節點都需要新的功能,因此可以做到按需升級和平滑過渡。
repeated:
表示該欄位可以包含0~N個元素。其特性和optional一樣,但是每一次可以包含多個值。可以看作是在傳遞一個數組的值。類比於Java這邊的List
protobuf定義了一套基本的數據類型,幾乎都映射了Java語言的基礎數據類型(主要以Java為例)
詳見下面表格
欄位的命名方式與Java的命名方式大致一致
欄位編碼是一個序列化和反序列化的標記值,有了該值,通信雙方才能互相識別對方的欄位。當然相同的編碼值,其限定修飾符和數據類型必須相同。編碼值的取值范圍為 1~2^32(4294967296)
其中 1~15的編碼時間和空間效率都是最高的,編碼值越大,其編碼的時間和空間效率就越低(相對於1-15),當然一般情況下相鄰的2個值編碼效率的是相同的,除非2個值恰好實在4位元組,12位元組,20位元組等的臨界區。比如15和16
1900~2000編碼值為Google protobuf 系統內部保留值,建議不要在自己的項目中使用。protobuf 還建議把經常要傳遞的值把其欄位編碼設置為1-15之間的值
完整的消息定義示例
枚舉的定義和Java 相同,使用 enum 關鍵字,但是有一些限制。
枚舉值必須大於等於0的整數。
使用分號 ; 分隔枚舉變數而不是Java 語言中的逗號 ,
示例:
你可以將其他消息類型用作欄位類型。已我們現在IM的為例,在每一個CSSendLiveRoomMsgReq消息中包含ImMsgBody消息,此時可以在相同的.proto文件中定義一個ImMsgBody消息類型,然後在CSSendLiveRoomMsgReq消息中指定一個ImMsgBody類型的欄位
示例:
如果你的消息中有很多可選欄位, 並且同時至多一個欄位會被設置, 你可以加強這個行為,使用oneof特性節省內存,Oneof欄位就像可選欄位, 除了它們會共享內存, 至多一個欄位會被設置。 設置其中一個欄位會清除其它欄位。
為了在.proto定義Oneof欄位, 你需要在名字前面加上oneof關鍵字, 比如下面例子的ImMsgBody:
oneof的特性
2. java 怎麼使用protobuf庫
1.到http://code.google.com/p/protobuf/downloads/list ,選擇其中的win版本下載,我選擇的是protoc-2.4.1-win32.zip
2.下載一個protobuf-java-2.4.1.jar文件(注意,要與你剛才下的proto.exe版本相同)
然後就開始開發了。
步驟:
1.用記事本編寫一個.proto文件:
}如:編寫的是test.proto
package protobuf;
option java_package = "com.sq.protobuf";
option java_outer_classname = "FirstProtobuf";
message testBuf {
required int32 ID = 1;
required string Url = 2;
}
將其放在與剛解壓的protoc.exe同級目錄中。
2.在cmd中,到protoc-2.4.1-win32文件夾下,
執行
E:\protoc-2.4.1-win32> protoc.exe --java_out=./ test.proto
則可以找到的一個生成的FirstProtobuf.java文件。
3.在MyEclipse中新建一個java project,建立包com.sq.protobuf,然後將剛才生成的FirstProtobuf.java文件放在其下面。
此時會報錯,因為沒有引入jar包,在package視圖下,將protobuf-java-2.4.1.jar引入,即可解決問題。
4.建立測試文件:
package com.sq.protobuf.test;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.sql.Blob;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import com.google.protobuf.;
import com.sq.protobuf.FirstProtobuf;
public class Test {
public static void main(String[] args) {
//序列化過程
//FirstProtobuf是生成類的名字,即proto文件中的java_outer_classname
//testBuf是裡面某個序列的名字,即proto文件中的message testBuf
FirstProtobuf.testBuf.Builder builder=FirstProtobuf.testBuf.newBuilder();
builder.setID(777);
builder.setUrl("shiqi");
//testBuf
FirstProtobuf.testBuf info=builder.build();
byte[] result = info.toByteArray() ;
String driver = "oracle.jdbc.driver.OracleDriver";
String url = "jdbc:oracle:thin:@10.64.59.12:1521/orcl";
String user = "parkingsystem";
String password = "parkingsystem";
try {
Class.forName(driver);
Connection conn = DriverManager.getConnection(url, user, password);
if(!conn.isClosed()){
System.out.println("Succeeded connecting to the Database!");
//此處只能使用prepareStatement
PreparedStatement ps = conn.prepareStatement("insert into test(id,test) values (1,?)");
//寫入資料庫,要把它改寫為流的形式
ByteArrayInputStream stream = new ByteArrayInputStream(result);
ps.setBinaryStream(1,stream,stream.available());
Statement statement = conn.createStatement();
Blob blob = null;
ps.execute();
////////////////上述完成將寫入資料庫的操作,資料庫中對應的欄位的屬性要設置為Blob
String sql = "select test from test";
ResultSet rs = statement.executeQuery(sql);
if(rs.next()){
blob = rs.getBlob("test");
}
byte[] s = blob.getBytes(1,(int)blob.length());
FirstProtobuf.testBuf testBuf = FirstProtobuf.testBuf.parseFrom(s);
System.out.println(testBuf);
conn.close();
}
}catch(Exception e) {
e.printStackTrace();
}
//反序列化過程
try {
FirstProtobuf.testBuf testBuf = FirstProtobuf.testBuf.parseFrom(result);
System.out.println(testBuf);
} catch ( e) {
e.printStackTrace();
}
}
}
發現可以將其序列化,插入到資料庫,並可以從資料庫出取出後,反序列化,內容可以正常顯示出來。
注意的就是2點:
1.不能用statement,否則無法插入blob類型的數據
2.為參數賦值時,要用
ByteArrayInputStream stream = new ByteArrayInputStream(result);
ps.setBinaryStream(1,stream,stream.available());
3. 作為java後端,在用protobuf的情況下我該給前端傳什麼形式的數據
protobuf是你們落地時存儲的數據格式,跟給前端的格式是沒有關系的。
可以通過工具類直接轉json後傳給前端。
Class<?> clazz = Class.forName(className);
Method method = clazz.getMethod("parseFrom", byte[].class);
JsonFormat().printToString((GeneratedMessage) method.invoke(clazz, bytes));
其中className是你根據protobuf文件生成的java類,bytes是讀取的protobuf對象的位元組數組。
4. 大數據技術是學什麼的
大數據技術是學這些:
編程語言
想要學習大數據技術,首先要掌握一門基礎編程語言。Java編程語言的使用率最廣泛,因此就業機會會更多一些,而Python編程語言正在高速推廣應用中,同時學習Python的就業方向會更多一些。
Linux
學習大數據一定要掌握一定的Linux技術知識,不要求技術水平達到就業的層次,但是一定要掌握Linux系統的基本操作。能夠處理在實際工作中遇到的相關問題。
SQL
大數據的特點就是數據量非常大,因此大數據的核心之一就是數據倉儲相關工作。因此大數據工作對於資料庫要求是非常的高。甚至很多公司單獨設置資料庫開發工程師。
Hadoop
Spark是專門為大規模數據處理而設計的快速通用的計算引擎。可以用它來完成各種各樣的運算,包括SQL查詢、文本處理、機器學習等等。
機器學習
機器學習是目前人工智慧領域的核心技術,在大數據專業中也有非常廣泛的引用。在演算法和自動化的發展過程中,機器學習扮演著非常重要的角色。可以大大拓展自己的就業方向。
互聯網行業里大數據和雲智能是當下最重要板塊,企業藉助大數據技術不僅能避免企業發展時會面臨的各種風險,更能解決發展過程中所遇到的種種難題。
對於想要學習大數據的更多信息,可以選擇到CDA 認證中心,是一套科學化,專業化,國際化的人才考核標准,共分為 CDA 、LEVELⅠ ,LEVEL Ⅱ,LEVEL Ⅲ三個等級,涉及行業包括互聯網、金融、咨詢、電信、零售、醫療、旅遊等,涉及崗位包括大數據、數據分析、市場、產品、運營、咨詢、投資、研發等。CDACDA(Certified Data Analyst),即「CDA 數據分析師」,
5. 如何提供java編譯protobuf協議文件的速度
ProtoBuf java 包編譯
ProtoBuf的官方下載包並不包含jar文件,需要用戶自己configure/make….來自行編譯。由於Windows上沒有編譯環境,就用了一個笨一點方法處理了。
分別下載:
protobuf-2.4.1.zip ProtoBuf的源文件(包含了C++/Java/Python)的源文件
protoc-2.4.1-win32.zip 已經編譯過的用於Windows平台的protoc命令(該命令用於將.proto文件轉化為Java或C++源文件)。
分別解析這兩個文件,你可以在protoc-2.4.1-win32.zip解壓後的文件中找到一個protoc.exe文件,將其到protobuf-2.4.1/src目錄下,然後進入protobuf-2.4.1/java,執行:
mvn install
如果沒有安裝maven的話,可以在網上找一下maven的安裝手冊。
編譯完成後可以在protobuf-2.4.1/java/target目錄中找到protobuf-2.4.1.jar文件.
後記
protobuf-2.4.1.zip裡面同時包含了protoc和java等的源文件,如果按照官方教程來安裝的話,就是先產生出protoc編譯器,然後再生成jar包。由於mvn install同時包含了編譯和測試過程,而測試代碼又依賴於protoc編譯器,所以就需要單獨下載protoc編譯器,並置於src目錄下了。
如果不需要驗證編譯結果(不執行單元測試),則可以如下:
下載protobuf-2.4.1.zip並解壓,進入到protobuf-2.4.1/java
mvn install –Dmaven.test.skip=true
這樣就可以了。
6. 了解一下ProtoBuf
我們在進行網路通信調用的時候,總是需要將內存的數據塊經過序列化,轉換成為一種可以通過網路流進行傳輸的格式。而這種格式在經過了傳輸之後再經過序列化,能還原成我們預想中的數據結構。
那麼我們對於這種用於中間網路傳輸的數據格式就有一定的要求。首先它可以准確地描述數據內容,在此基礎上我們則希望它盡量的小。
最開始流行起來的是XML,可擴展標記語言。由於它可以用來標記數據、定義數據類型,所以用戶可以自己定義數據自己的語言,從而讓對不同的數據結構化成統一的格式稱為了可能。
而另外一個我們熟知的則是JSON(JavaScript Object Notation, JS 對象簡譜)。盡管JSON中缺少了XML中的標簽屬性等描述方式,但是足夠簡介和清晰的層次結構使得其成為了必XML更受歡迎的數據交換格式。
同一份數據顯然JSON的數據量比XML所使用的空間更少。那麼空間省略在哪裡呢?一方面是json使用更簡單的字元來定義數據間的關聯關系;另一方面是JSON減少了對數據類型的描述。但是丟少的數據類型再哪裡呢?
以Java中的 OpenFeign 舉例,JSON中缺少的類型定義被定義道程序中的介面中了。當進行序列化與反序列化時,JSON格式並不記錄數據的類型,具體的數據類型在序列化方與反序列化方通過事先約定的介面來進行定義。這樣就減少了信息傳輸過程中的信息量,從而讓數據得以壓縮。
但是JSON由於沒有定義數據類型,所以在傳輸的過程中實際上就都是文本流,那麼這種方法還可以進一步壓縮嗎?
結合上文的討論,我們先說結論:方法是有的,並寫當前的實現方式是ProtoBuf。但在此之前我們先來了解一下ProtoBuf。
我們可以先看看官方給出的定義與描述:
同樣的,ProtoBuf也是一種支持序列化反序列化的方法,並且他具有很多優點:
實際上,ProtoBuf提供了一種通用的數據描述方式,這種定義數據的方式是通用的,就如同JSON或者XML一樣。
接下來我們來來回答本節一開始的問題,針對JSON來說,ProtoBuf是如何將體積變得更小的呢?答案很簡單,就是為數據序列化反序列化提供更多的先驗知識。
本文暫不過度深入ProtoBuf原理,但是可以通過一張圖來進行簡要說明():
ProtoBuf中的數據是按順序進行排列,而整體的結構為若干個field,每一個field中由 Tag-[Length]-Value 組成。Length是可選的,而是否存在Length是通過Tag的類型來決定的。也就是說如果是指定的類型,比如int64,那我們就可以知道Value的長度,也就不用在依靠Length來對其空間進行描述(redis中的壓縮列表也是這個思想)。
那麼field應該對應的是什麼欄位呢?這個則是在序列化與反序列化時在ProtoBuf的服務端與客戶端之間進行預先定義的。而因為提前定義了field的類型、排序,所以field本身可以不用對欄位名、欄位位置進行描述,只需要根據欄位類型選用合適的二進制序列化方法,將欄位本身的value值進行序列化傳輸即可。
稍微總結一下:
ProtoBuf通過對傳輸欄位的名稱、順序進行預定義,從而在傳輸結構中只需要順序的記錄每個欄位的類型標簽和二進制值。
盡管上文和官方中都是以XML或者JSON來對ProtoBuf進行對比。但是因為ProtoBuf本身就是二進制序列化方式,所以從壓縮比上比較感覺有點欺負人。
對應的在Java中二進制常用的序列化器有Kryo和Hessian。但事實上,由於Kryo和Hessian中都需要對Java類名和欄位信息進行存儲。而ProtoBuf則只有Tag-Length-Value的數據對,且Value更是有針對性的特殊編碼,所以空間佔用小的很多。
Kryo是專門針對Java進行優化了的。所以在使用的便捷性上來說Kryo則更加方便。但ProtoBuf是跨平台的,且由於進行了欄位的順序定義,所以似的ProtoBuf定義後的介面是可以向前兼容的(只向後追加欄位),而這種優勢是Kryo所沒有的。
ProtoBuf是跨語言的,使用ProtoBuf的第一步是先定一個 proto 文件 ,而由於ProtoBuf 2和3語言版本的不同,其定義格式會有所不同,具體的細節還是得參考官方文檔:https://developers.google.cn/protocol-buffers/docs/proto3
對於ProtoBuf 3 的定義文檔我們可以按如下方法定義:
其中message關鍵字是定義的文件名,而 string、int32則是預定的欄位類型,repeated則是描述欄位為可重復任意多次的欄位。
ProtoBuf通過這種形式的文件定義了傳輸信息的文件結構。
但是之前小節中我們知道了ProtoBuf是通過 Tag-[Length]-Value 組成的數據組來進行信息傳輸的,那麼proto文件中定義的內容如何轉換為實際傳輸的對象呢?
ProtoBuf的做法是,為每一種語言提供一個生成器protoc。通過使用protoc則可以根據.proto文件生成為一組java文件。對應的官方語法演示樣例為:
官方的生成參考為:https://developers.google.com/protocol-buffers/docs/reference/java-generated
生成後的java文件將提供對應的實體以及數據的構造方法等文件,從而支持後續的使用。
需要注意的是,ProtoBuf是本質上是序列化方法,具體是通過Spring Cloud 的OpenFeign進行介面調用,還是通過grpc進行介面調用,都是可以的。
本文對ProtoBuff進行了概念的整理,並沒有對每個細節都進行深入的梳理,可以當作概念科普來進行閱讀。
7. 如何查看php是否支持protobuf
首先要知道,protobuf是google提供的一個開源序列化框架,類似於XML,JSON這樣的數據表示語言,詳情訪問protobuf的google官方網站。
為什麼要使用呢!
使用protobuf主要是, 一條消息數據,用protobuf序列化後的大小是json的10分之一,xml格式的20分之一,是二進制序列化的10分之一。
其次要知道,protobuf原生支持程序語言c++,java,python;其它的語言需要第三方或者自己寫,序列化和反序列化的效率不保證。若是真要在php中使用,網路也很多,層次不齊,可斟酌!
參考:
https://github.com/protobuf-php/protobuf
8. protobuffer java中文亂碼怎麼解決
protobuf支持非UTF8字元串
protobuf規范string類型是必須是UTF8字元,但在C/C++中可以直接調用set方法設置任意編碼方式的字元串,也可以直接取得對應字元串,但在控制台中會列印出編碼不是UTF8字元的錯誤信息.
查看protobuf源代碼發現是在wire_format.h中有一函數VerifyUTF8String()里進行編碼判斷的,而且有一宏定義GOOGLE_PROTOBUF_UTF8_VALIDATION_ENABLED可以取消此錯誤信息.估計google當初開發時用的是std::string類型,並沒有編碼方面的強制要求,只是在跨平台時沒有統一編碼容易引起問題,才統一使用UTF8方式傳送字元串.
但像Java,Python預設就是支持UNICODE,在protobuf庫中就已經做了轉換或檢測,可以修改相關代碼不做此轉換或檢測.
如python中修改lib中的protobufxxx.egg中的decoder.py的StringDecoder()方法,將value.append(local_unicode(buffer[pos:new_pos], 'utf-8'))
改為value.append(buffer[pos:new_pos])
,將field_dict[key] = local_unicode(buffer[pos:new_pos], 'utf-8')
改為field_dict[key] = buffer[pos:new_pos]
即可,Python即不會報異常錯誤,也能正確取得任意編碼的字元串,但需要注意取出後需要進行編碼(decode("gbk"))才能正確顯示.
另外type_checkers.py中CheckValue()中對str的判斷也需要去掉,encoder.py中帶'utf-8'的全改了,才能正常編碼.
至於如此改會不會有其它潛在的問題,還有待測試.
9. php protobuf不能為null嗎
不能。
phpprotobuf必須滿是程序數字不可以添加空的值。
phpProtocol是一種輕便高效的結構化數據存儲格式,平台無關、語言無關、可擴展,可用於通訊協議和數據存儲等領域。
null是在計算中具有保留的值,用於指示指針不引用有效對象。
10. web後端開發需要什麼技術
1.腳本語言基礎 主流的有php java .net 非主流的有python ruby 還有最近出現的node.js golang 任一即可
2.資料庫基礎 後端就是跟資料庫打交道的 一般學習關系型資料庫即可
3.伺服器基礎 後端代碼是運行在伺服器上的 不像前端運行在客戶瀏覽器 所以你需要掌握少許的伺服器基礎 至少要會用
4.以上三點滿足即可開始後端開發 但是要提高還需要學會 緩存 隊列應用 跨平台請求 分布式 等等 可以說 後端入門容易 但很寬也很廣 只能一點點學習