上一篇文章介紹的是nginx做爲負載均衡與後端grpc的集成,沒有繼續深入下去,因爲這個需要線上真正實踐纔會有更多的感悟,之後入職後有體會的話再繼續寫吧。
剛好我也比較好奇protobuf到底和grpc是個什麼關係,protobuf在整個rpc的過程中起的是什麼作用,所以我之後的幾篇文章都會是關於protobuf的。
我還是從網上找了幾個網上的資料,看看前人的研究成果:
proto3官網 毫不猶豫推薦官網的文章
Google Protocol Buffer 的使用和原理 這個文章有點老,介紹的不是proto3,但是作者研究的挺深入的。
proto3語言指南 這個有點像官網英文的翻譯和作者自己的總結,也非常不錯
什麼是ProtoBuf
摘抄一段別人的描述:Protocol Buffers 是一種輕便高效的結構化數據存儲格式,可以用於結構化數據串行化,或者說序列化。它很適合做數據存儲或 RPC 數據交換格式。可用於通訊協議、數據存儲等領域的語言無關、平臺無關、可擴展的序列化結構數據格式。看到這個描述第一感覺是雲裏霧裏,研究後總結protobuf的以下幾點:
- grpc作爲rpc的一種,肯定有服務間的網絡調用,調用就一定會有數據的傳輸,而這個數據的傳輸就用的是protobuf去序列化和反序列化的,一個請求的內容在調用方序列化通過網絡傳送到服務方,服務方就能用protobuf反序列化出來,後面的文章會講解細節。
- protobuf有一個類似thrift的IDL(接口描述語言),就是描述一個對象包括什麼字段,一個服務包括什麼接口,簡單如下:
message Person {
string name = 1;
int32 id = 2;
string email = 3;
}
- protobuf還有專門的工具生成對象代碼,你用這些代碼就能將對象序列化到文件或者流中,當然也能反序列化成對象,後面的例子會具體介紹。
- protobuf支持多種語言,常見的比如go,c++,java,php等,具體可以參考官網
怎麼使用ProtoBuf
準備工作
與前面介紹grpc使用的步驟一樣,現在準備一個.proto文件
syntax = "proto3"; package tutorial; option java_package = "com.example.tutorial"; option java_outer_classname = "AddressBookProtos"; message Person { string name = 1; int32 id = 2; string email = 3; enum PhoneType { MOBILE = 0; HOME = 1; WORK = 2; } message PhoneNumber { string number = 1; PhoneType type = 2; } repeated PhoneNumber phones = 4; } message AddressBook { repeated Person people = 1; }
使用前面文章介紹的maven plugin生成代碼。
拷貝代碼
將生成好的代碼AddressBookProtos.java拷貝到你新建的工程,類似如下結構:
序列化對象
參考官網的說明,新建一個WriteObject.java, 代碼如下,通過輸入逐漸構造Person對象,並寫入一個文件,記得在啓動參數里加上文件的路徑
static Person PromptForAddress(BufferedReader stdin, PrintStream stdout) throws IOException { Person.Builder person = Person.newBuilder(); stdout.print("Enter person ID: "); person.setId(Integer.valueOf(stdin.readLine())); stdout.print("Enter name: "); person.setName(stdin.readLine()); stdout.print("Enter email address (blank for none): "); String email = stdin.readLine(); if (email.length() > 0) { person.setEmail(email); } while (true) { stdout.print("Enter a phone number (or leave blank to finish): "); String number = stdin.readLine(); if (number.length() == 0) { break; } Person.PhoneNumber.Builder phoneNumber = Person.PhoneNumber.newBuilder().setNumber(number); stdout.print("Is this a mobile, home, or work phone? "); String type = stdin.readLine(); if (type.equals("mobile")) { phoneNumber.setType(Person.PhoneType.MOBILE); } else if (type.equals("home")) { phoneNumber.setType(Person.PhoneType.HOME); } else if (type.equals("work")) { phoneNumber.setType(Person.PhoneType.WORK); } else { stdout.println("Unknown phone type. Using default."); } person.addPhones(phoneNumber); } return person.build(); } public static void main(String[] args) throws IOException { if (args.length != 1) { System.err.println("Usage: AddPerson ADDRESS_BOOK_FILE"); System.exit(-1); } AddressBook.Builder addressBook = AddressBook.newBuilder(); // Read the existing address book. try { addressBook.mergeFrom(CodedInputStream.newInstance(new FileInputStream(args[0]))); } catch (FileNotFoundException e) { System.out.println(args[0] + ": File not found. Creating a new file."); } // Add an address. addressBook.addPeople( PromptForAddress(new BufferedReader(new InputStreamReader(System.in)), System.out)); // Write the new address book back to disk. FileOutputStream output = new FileOutputStream(args[0]); addressBook.build().writeTo(output); output.close(); }
運行後用sublime打開寫入的文件如下, 具體爲什麼是這種後面我會具體研究,現在所要知道的是這種序列化的方式肯定比一般的xml和json要節省空間的
反序列化對象
參考官網新建ReadObject.java, 代碼如下, 讀取同樣的文件,反序列化後就能取出對應的字段值
static void Print(AddressBook addressBook) { for (Person person: addressBook.getPeopleList()) { System.out.println("Person ID: " + person.getId()); System.out.println(" Name: " + person.getName()); for (Person.PhoneNumber phoneNumber : person.getPhonesList()) { switch (phoneNumber.getType()) { case MOBILE: System.out.print(" Mobile phone #: "); break; case HOME: System.out.print(" Home phone #: "); break; case WORK: System.out.print(" Work phone #: "); break; } System.out.println(phoneNumber.getNumber()); } } } public static void main(String[] args) throws IOException { if (args.length != 1) { System.err.println("Usage: ListPeople ADDRESS_BOOK_FILE"); System.exit(-1); } // Read the existing address book. AddressBook addressBook = AddressBook.parseFrom(new FileInputStream(args[0])); Print(addressBook); }
運行一下結果如下:
Person ID: 1
Name: zack
總結
至此,大概瞭解了一下怎麼單獨使用protobuf以及它在grpc中的作用,後面我會繼續研究protobuf3的語法細節和序列化細節
另附文章中提到的工程文件:
歡迎關注我的個人的博客www.zhijianliu.cn, 虛心求教,有錯誤還請指正輕拍,謝謝
版權聲明:本文出自志健的原創文章,未經博主允許不得轉載