l 定義服務(Service)
如果想要將消息類型用在RPC(遠程方法調用)系統中,可以在.proto文件中定義一個RPC服務接口,protocol buffer編譯器將會根據所選擇的不同語言生成服務接口代碼及存根。如,想要定義一個RPC服務並具有一個方法,該方法能夠接收 SearchRequest並返回一個SearchResponse,此時可以在.proto文件中進行如下定義:
service SearchService { rpc Search (SearchRequest) returns (SearchResponse); } |
protocol編譯器將產生一個抽象接口SearchService以及一個相應的存根實現。存根將所有的調用指向RpcChannel,它是一 個抽象接口,必須在RPC系統中對該接口進行實現。如,可以實現RpcChannel以完成序列化消息並通過HTTP方式來發送到一個服務器。換句話說, 產生的存根提供了一個類型安全的接口用來完成基於protocolbuffer的RPC調用,而不是將你限定在一個特定的RPC的實現中。C++中的代碼 如下所示:
using google::protobuf; protobuf::RpcChannel* channel; void DoSearch() { // The protocol compiler generates the SearchService class based on the service = new SearchService::Stub(channel); // Execute the RPC. void Done() { |
所有service類都必須實現Service接口,它提供了一種用來調用具體方法的方式,即在編譯期不需要知道方法名及它的輸入、輸出類型。在服務器端,通過服務註冊它可以被用來實現一個RPC Server。
using google::protobuf; |
l 選項(Options)
在定義.proto文件時能夠標註一系列的options。Options並不改變整個文件聲明的含義,但卻能夠影響特定環境下處理方式。完整的可用選項可以在google/protobuf/descriptor.proto
找到。
一些選項是文件級別的,意味着它可以作用於最外範圍,不包含在任何消息內部、enum或服務定義中。一些選項是消息級別的,意味着它可以用在消息定 義的內部。當然有些選項可以作用在域、enum類型、enum值、服務類型及服務方法中。到目前爲止,並沒有一種有效的選項能作用於所有的類型。
如下就是一些常用的選擇:
² java_package
(file option): 這個選項表明生成java類所在的包。如果在.proto文件中沒有明確的聲明java_package,就採用默認的包名。當然了,默認方式產生的 java包名並不是最好的方式,按照應用名稱倒序方式進行排序的。如果不需要產生java代碼,則該選項將不起任何作用。如:
|
² java_outer_classname
(file option): 該選項表明想要生成Java類的名稱。如果在.proto文件中沒有明確的java_outer_classname
定義,生成的class名稱將會根據.proto文件的名稱採用駝峯式的命名方式進行生成。如(foo_bar.proto生成的java類名爲FooBar.java),如果不生成java代碼,則該選項不起任何作用。如:
|
² optimize_for (fileoption): 可以被設置爲 SPEED, CODE_SIZE,or LITE_RUNTIME。這些值將通過如下的方式影響C++及java代碼的生成:
· SPEED (default): protocol buffer編譯器將通過在消息類型上執行序列化、語法分析及其他通用的操作。這種代碼是最優的。
· CODE_SIZE: protocol buffer編譯器將會產生最少量的類,通過共享或基於反射的代碼來實現序列化、語法分析及各種其它操作。採用該方式產生的代碼將比SPEED要少得多, 但是操作要相對慢些。當然實現的類及其對外的API與SPEED模式都是一樣的。這種方式經常用在一些包含大量的.proto文件而且並不盲目追求速度的 應用中。
· LITE_RUNTIME: protocol buffer編譯器依賴於運行時核心類庫來生成代碼(即採用libprotobuf-lite 替代libprotobuf)。這種核心類庫由於忽略了一 些描述符及反射,要比全類庫小得多。這種模式經常在移動手機平臺應用多一些。編譯器採用該模式產生的方法實現與SPEED模式不相上下,產生的類通過實現 MessageLite接口,但它僅僅是Messager接口的一個子集。
|
² cc_generic_services
, java_generic_services
, py_generic_services
(file options): 在C++、java、python中protocol buffer編譯器是否應該基於服務定義產生抽象服務代碼。由於歷史遺留問題,該值默認是true。但是自2.3.0版本以來,它被認爲通過提供代碼生成
器插件來對RPC實現更可取,而不是依賴於“抽象”服務。
|
² message_set_wire_format
(message option):如果該值被設置爲true,該消息將使用一種不同的二進制格式來與Google內部的MessageSet的老格式相兼容。對於Google外部的用戶來說,該選項將不會被用到。如下所示:
|
² packed
(field option): 如果該選項在一個整型基本類型上被設置爲真,則採用更緊湊的編碼方式。當然使用該值並不會對數值造成任何損失。在2.3.0版本之前,解析器將會忽略那些 非期望的包裝值。因此,它不可能在不破壞現有框架的兼容性上而改變壓縮格式。在2.3.0之後,這種改變將是安全的,解析器能夠接受上述兩種格式,但是在 處理protobuf老版本程序時,還是要多留意一下。
|
² deprecated
(field option): 如果該選項被設置爲true,表明該字段已經被棄用了,在新代碼中不建議使用。在多數語言中,這並沒有實際的含義。在java中,它將會變成一個 @Deprecated
註釋。也許在將來,其它基於語言聲明的代碼在生成時也會如此使用,當使用該字段時,編譯器將自動報警。如:
|
Ø 自定義選項
ProtocolBuffers允許自定義並使用選項。該功能應該屬於一個高級特性,對於大部分人是用不到的。由於options是定在 google/protobuf/descriptor.proto中的,因此你可以在該文件中進行擴展,定義自己的選項。如:
|
在上述代碼中,通過對MessageOptions進行擴展定義了一個新的消息級別的選項。當使用該選項時,選項的名稱需要使用()包裹起來,以表明它是一個擴展。在C++代碼中可以看出my_option是以如下方式被讀取的。
|
在Java代碼中的讀取方式如下:
|
正如上面的讀取方式,定製選項對於Python並不支持。定製選項在protocol buffer語言中可用於任何結構。下面就是一些具體的例子:
|
注:如果要在該選項定義之外使用一個自定義的選項,必須要由包名 + 選項名來定義該選項。如:
|
最後一件事情需要注意:因爲自定義選項是可擴展的,它必須象其它的域或擴展一樣來定義標識號。正如上述示例,[50000-99999]已經被佔 用,該範圍內的值已經被內部所使用,當然了你可以在內部應用中隨意使用。如果你想在一些公共應用中進行自定義選項,你必須確保它是全局唯一的。可以通過[email protected]來獲取全局唯一標識號。
l 生成訪問類
可以通過定義好的.proto文件來生成Java、Python、C++代碼,需要基於.proto文件運行protocol buffer編譯器protoc。運行的命令如下所示:
|
· IMPORT_PATH聲明瞭一個.proto文件所在的具體目錄。如果忽略該值,則使用當前目錄。如果有多個目錄則可以 對--proto_path 寫多次,它們將會順序的被訪問並執行導入。-I=IMPORT_PATH是它的簡化形式。
· 當然也可以提供一個或多個輸出路徑:
o --cpp_out 在目標目錄DST_DIR中產生C++代碼,可以在 http://code.google.com/intl/zh-CN/apis/protocolbuffers/docs/reference /cpp-generated.html中查看更多。
o --java_out 在目標目錄DST_DIR中產生Java代碼,可以在 http://code.google.com/intl/zh-CN/apis/protocolbuffers/docs/reference /java-generated.html中查看更多。
o --python_out 在目標目錄 DST_DIR 中產生Python代碼,可以在http://code.google.com/intl/zh-CN/apis/protocolbuffers /docs/reference/python-generated.html中查看更多。
作爲一種額外的使得,如果DST_DIR 是以.zip或.jar結尾的,編譯器將輸出結果打包成一個zip格式的歸檔文件。.jar將會輸出一個 Java JAR聲明必須的manifest文件。注:如果該輸出歸檔文件已經存在,它將會被重寫,編譯器並沒有做到足夠的智能來爲已經存在的歸檔文件添加新的文 件。
· 你必須提供一個或多個.proto文件作爲輸入。多個.proto文件能夠一次全部聲明。雖然這些文件是相對於當前目錄來命名的,每個文件必須在一個IMPORT_PATH中,只有如此編譯器纔可以決定它的標準名稱。