1. 程式人生 > >google protobuf 定義服務(service)

google protobuf 定義服務(service)

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;
protobuf::RpcController* controller;
SearchService* service;
SearchRequest request;
SearchResponse response;

void DoSearch() {
  // You provide classes MyRpcChannel and MyRpcController, which implement
  // the abstract interfaces protobuf::RpcChannel and protobuf::RpcController.
  channel = new MyRpcChannel("somehost.example.com:1234");
  controller = new MyRpcController;
  

// The protocol compiler generates the SearchService class based on the
  // definition given above.
 

service = new SearchService::Stub(channel);
  // Set up the request.
  request.set_query("protocol buffers");

  // Execute the RPC.
  service->Search(controller, request, response, protobuf::NewCallback(&Done));
}

void Done() {
  delete service;
  delete channel;
  delete controller;
}

所有service類都必須實現Service介面,它提供了一種用來呼叫具體方法的方式,即在編譯期不需要知道方法名及它的輸入、輸出型別。在伺服器端,通過服務註冊它可以被用來實現一個RPC Server。

using google::protobuf;

class ExampleSearchService : public SearchService {
 public:
  void Search(protobuf::RpcController* controller,
              const SearchRequest* request,
              SearchResponse* response,
              protobuf::Closure* done) {
    if (request->query() == "google") {
      response->add_result()->set_url("http://www.google.com");
    } else if (request->query() == "protocol buffers") {
      response->add_result()->set_url("http://protobuf.googlecode.com");
    }
    done->Run();
  }
};

int main() {
  // You provide class MyRpcServer.  It does not have to implement any
  // particular interface; this is just an example.
  MyRpcServer server;

  protobuf::Service* service = new ExampleSearchService;
  server.ExportOnPort(1234, service);
  server.Run();

  delete service;
  return 0;
}

l  選項(Options)

在定義.proto檔案時能夠標註一系列的options。Options並不改變整個檔案宣告的含義,但卻能夠影響特定環境下處理方式。完整的可用選項可以在google/protobuf/descriptor.proto找到。

一些選項是檔案級別的,意味著它可以作用於最外範圍,不包含在任何訊息內部、enum或服務定義中。一些選項是訊息級別的,意味著它可以用在訊息定 義的內部。當然有些選項可以作用在域、enum型別、enum值、服務型別及服務方法中。到目前為止,並沒有一種有效的選項能作用於所有的型別。

如下就是一些常用的選擇:

²  java_package (file option): 這個選項表明生成java類所在的包。如果在.proto檔案中沒有明確的宣告java_package,就採用預設的包名。當然了,預設方式產生的 java包名並不是最好的方式,按照應用名稱倒序方式進行排序的。如果不需要產生java程式碼,則該選項將不起任何作用。如:

option java_package = "com.example.foo";

²  java_outer_classname (file option): 該選項表明想要生成Java類的名稱。如果在.proto檔案中沒有明確的java_outer_classname定義,生成的class名稱將會根據.proto檔案的名稱採用駝峰式的命名方式進行生成。如(foo_bar.proto生成的java類名為FooBar.java),如果不生成java程式碼,則該選項不起任何作用。如:

option java_outer_classname = "Ponycopter";

²  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介面的一個子集。

option optimize_for = CODE_SIZE;

²  cc_generic_servicesjava_generic_servicespy_generic_services (file options): 在C++、java、python中protocol buffer編譯器是否應該基於服務定義產生抽象服務程式碼。由於歷史遺留問題,該值預設是true。但是自2.3.0版本以來,它被認為通過提供程式碼生成 器外掛來對RPC實現更可取,而不是依賴於“抽象”服務。

// This file relies on plugins to generate service code.

option cc_generic_services = false;

option java_generic_services = false;

option py_generic_services = false;

²  message_set_wire_format (message option):如果該值被設定為true,該訊息將使用一種不同的二進位制格式來與Google內部的MessageSet的老格式相相容。對於Google外部的使用者來說,該選項將不會被用到。如下所示:

message Foo {

  option message_set_wire_format = true;

  extensions 4 to max;

}

²  packed (field option): 如果該選項在一個整型基本型別上被設定為真,則採用更緊湊的編碼方式。當然使用該值並不會對數值造成任何損失。在2.3.0版本之前,解析器將會忽略那些 非期望的包裝值。因此,它不可能在不破壞現有框架的相容性上而改變壓縮格式。在2.3.0之後,這種改變將是安全的,解析器能夠接受上述兩種格式,但是在 處理protobuf老版本程式時,還是要多留意一下。

repeated int32 samples = 4 [packed=true];

²  deprecated (field option): 如果該選項被設定為true,表明該欄位已經被棄用了,在新程式碼中不建議使用。在多數語言中,這並沒有實際的含義。在java中,它將會變成一個 @Deprecated註釋。也許在將來,其它基於語言宣告的程式碼在生成時也會如此使用,當使用該欄位時,編譯器將自動報警。如:

optional int32 old_field = 6 [deprecated=true];

Ø 自定義選項

ProtocolBuffers允許自定義並使用選項。該功能應該屬於一個高階特性,對於大部分人是用不到的。由於options是定在 google/protobuf/descriptor.proto中的,因此你可以在該檔案中進行擴充套件,定義自己的選項。如:

import "google/protobuf/descriptor.proto";

extend google.protobuf.MessageOptions {

  optional string my_option = 51234;

}

message MyMessage {

  option (my_option) = "Hello world!";

}

在上述程式碼中,通過對MessageOptions進行擴充套件定義了一個新的訊息級別的選項。當使用該選項時,選項的名稱需要使用()包裹起來,以表明它是一個擴充套件。在C++程式碼中可以看出my_option是以如下方式被讀取的。

string value = MyMessage::descriptor()->options().GetExtension(my_option);

在Java程式碼中的讀取方式如下:

String value = MyProtoFile.MyMessage.getDescriptor().getOptions().getExtension(MyProtoFile.myOption);

正如上面的讀取方式,定製選項對於Python並不支援。定製選項在protocol buffer語言中可用於任何結構。下面就是一些具體的例子:

import "google/protobuf/descriptor.proto";

extend google.protobuf.FileOptions {

  optional string my_file_option = 50000;

}

extend google.protobuf.MessageOptions {

  optional int32 my_message_option = 50001;

}

extend google.protobuf.FieldOptions {

  optional float my_field_option = 50002;

}

extend google.protobuf.EnumOptions {

  optional bool my_enum_option = 50003;

}

extend google.protobuf.EnumValueOptions {

  optional uint32 my_enum_value_option = 50004;

}

extend google.protobuf.ServiceOptions {

  optional MyEnum my_service_option = 50005;

}

extend google.protobuf.MethodOptions {

  optional MyMessage my_method_option = 50006;

}

option (my_file_option) = "Hello world!";

message MyMessage {

  option (my_message_option) = 1234;

  optional int32 foo = 1 [(my_field_option) = 4.5];

  optional string bar = 2;

}

enum MyEnum {

  option (my_enum_option) = true;

  FOO = 1 [(my_enum_value_option) = 321];

  BAR = 2;

}

message RequestType {}

message ResponseType {}

service MyService {

  option (my_service_option) = FOO;

  rpc MyMethod(RequestType) returns(ResponseType) {

    // Note:  my_method_option has type MyMessage.  We can set each field

    //   within it using a separate "option" line.

    option (my_method_option).foo = 567;

    option (my_method_option).bar = "Some string";

  }

}

注:如果要在該選項定義之外使用一個自定義的選項,必須要由包名 + 選項名來定義該選項。如:

// foo.proto

import "google/protobuf/descriptor.proto";

package foo;

extend google.protobuf.MessageOptions {

  optional string my_option = 51234;

}

// bar.proto

import "foo.proto";

package bar;

message MyMessage {

  option (foo.my_option) = "Hello world!";

}

最後一件事情需要注意:因為自定義選項是可擴充套件的,它必須象其它的域或擴充套件一樣來定義標識號。正如上述示例,[50000-99999]已經被佔 用,該範圍內的值已經被內部所使用,當然了你可以在內部應用中隨意使用。如果你想在一些公共應用中進行自定義選項,你必須確保它是全域性唯一的。可以通過[email protected]來獲取全域性唯一標識號。

l  生成訪問類

可以通過定義好的.proto檔案來生成Java、Python、C++程式碼,需要基於.proto檔案執行protocol buffer編譯器protoc。執行的命令如下所示:

protoc --proto_path=IMPORT_PATH --cpp_out=DST_DIR --java_out=DST_DIR --python_out=DST_DIR path/to/file.proto

·        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中,只有如此編譯器才可以決定它的標準名稱。

相關推薦

google protobuf 定義服務service

l  定義服務(Service) 如果想要將訊息型別用在RPC(遠端方法呼叫)系統中,可以在.proto檔案中定義一個RPC服務介面,protocol buffer編譯器將會根據所選擇的不同語言生成服務介面程式碼及存根。如,想要定義一個RPC服務並具有一個方法,該方法能

記一次在廣播BroadcastReceiver服務Service裏彈窗的“完美”實踐

dac target 百度 define key 捕獲 只有一個 show 一個 事情是這樣的,目前在做一個醫療項目,需要定時在某個時間段比如午休時間和晚上讓我們的App休眠,那麽這個時候在休眠時間段如果用戶按了電源鍵點亮屏幕了,我們就需要彈出一個全屏的窗口去做一個人性化

Android廣播BroacastReceiver服務Service

com 還要 activit pro service 動態註冊 let onclick nds BroadcastReceiver可以理解成是一種組件,是默默的在改後臺運行的,用於在不同軟件和不同組件之間的傳遞,無法被用戶感知,因為他在系統的內部工作,BroadcastRe

Angular學習筆記9:服務Service2

可觀察的物件(observable)的資料 在現在的是情況下:HeroService.getHeroes() 的函式簽名是同步的,它所隱含的假設是 HeroService 總是能同步獲取英雄列表資料。 而 HeroesComponent 也同樣假

angular中的$http服務service解析

1.1. 前述: ng中的$http用於請求後臺資料,類似於js用的ajax和jq中的$.ajax 在ng的$http中的方法有 0、$http 1

Android 服務service的生命週期以及利用bindservice呼叫服務裡面的方法

服務的生命週期 服務的生命週期跟啟動服務的方法有關:  當採用Context.startService()方法啟動服務,與之有關的生命週期方法 onCreate() onStart()  onDestroy() onCreate()該方法在服務被建立時呼叫,該方法只會被

android 系統服務service

-服務(service)是Android 系統中的重要元件,服務可以在不顯示介面的情況下在後臺執行制定的任務或者進行兩個不同程序間的通訊, android在後臺執行著許多服務,這些服務在系統啟動時被開啟,支援系統的正常工作。例如:來電顯示服務,在程式設計是通過

Android中的服務service詳解

1. 引言:在Android系統中,到處可見service(服務)這個單詞,從功能上來講,它意味著沒有UI介面,作為一個後臺程序,執行一些特定的任務。在Android應用開發過程中,也免不了需要開發一些service來完成一些功能,而這種應用層的service(繼承看自se

Android 活動Activity服務Service、程序Process的生命週期

在 Android 中,多數情況下每個程式都是在各自獨立的 Linux 程序中執行的。當一個程式或其某些部分被請求時,它的程序就“出生”了;當這個程式沒有必要再執行下去且系統需要回收這個程序的記憶體用於其他程式時,這個 程序就“死亡”了。可以看出,Android 程式的生

ambari自定義服務metainfo.xml說明

 Metainfo.xml宣告性定義Ambari服務。這是服務定義最關鍵的檔案。下面說明Metainfo.xml各部分的作用。 描述服務<service>的欄位包括: 屬性 描述 name 服務名,必須唯一 displayName 服務在web UI上的顯

Dubbo原始碼分析:Dubbo之服務Service

         如上圖所示的Dubbo的暴露服務的過程,不難看出它也和消費者端很像,也需要一個像reference的物件來維護service關聯的所有物件及其屬性,這裡的reference就是provider。由於ServiceBean實現了  Initializ

Android Service 服務—— Service

                一、 Service簡介Service是android 系統中的四大元件之一(Activity、Service、BroadcastReceiver、ContentProvider),它跟Activity的級別差不多,但不能自己執行只能後臺執行,並且可以和其他元件進行互動。ser

軟體定義儲存服務 SDSS

我們的雲端計算資源池是從2011年開始建設,承載了很多核心業務平臺。為了防止“雞蛋放在一個籃子裡”,2013年建成了超遠距離雙活的資源池體系。但是,配置效率、資源靈活性、擴充套件性等問題是資源池發展的瓶頸。2013年我們在資源池內部署了SDN,並提出了基於X86伺服器+SATA硬碟+軟體定義的分散式

ros編寫自定義服務.srv

1、編寫自定義的服務檔案(即.srv)檔案。 2、儲存,catkin_make編譯,要生成可以被#include "包/AddTwoInts.h",注意CMakeList.txt檔案的配置 cmake_minimum_required(VERSION 2.8.3) project(le

service服務詳解

##01_start開啟服務的生命週期(重點) 服務的特點: 生命週期的方法: onCreate: 初始化service的例項物件; onStartCommand:開啟服務; onDestroy:銷燬例項物件之前呼叫這個方法做掃尾工作; onResume、onPa

服務Microservices服務網格Service Mesh架構概念整理

注:文章內容為摘錄性文字,自己閱讀的一些筆記,方便日後檢視。 微服務(Microservices) 在過去的 2016 年和 2017 年,微服務技術迅猛普及,和容器技術一起成為這兩年中最吸引眼球的技術熱點。而以 Spring Cloud 為代表的傳統侵入式開發框架,佔據著微服務市場的主流地位。 微服務(Mi

【Consul】Consul實踐指導-服務註冊Service

        服務發現是consul的另一主要功能。Consul Agent提供簡單的Service定義格式用於申報服務的可用性,並與健康檢查相關聯。如果健康檢查與服務關聯,則認為服務是應用級的。服務可以定義在配置檔案中或在執行時通過HTTP介面新增。  1.1.1  S

如何使用Azure Container Service Engine在Azure中國區部署容器服務:DC/OS篇

前言 什麼是Azure Container Service(簡稱ACS) 目前越來越多的企業正在嘗試使用容器來構建他們的服務,他們或者在自己的本地資料中心搭建基於容器的叢集,或者利用公有云來承載基於容器和微服務的叢集架構,然而很多企業發現搭建一套能夠適用

SpringBoot之退出服務exit時呼叫自定義的銷燬方法

我們在工作中有時候可能會遇到這樣場景,需要在退出容器的時候執行某些操作。SpringBoot中有兩種方法可以供我們來選擇(其實就是spring中我們常用的方式。只是destory-method是在XM

ArcGIS For JavaScript API 新增一個dynamic Service(動態服務)————

描述: 此示例演示如何新增一個地圖繪製的伺服器在使用者每次縮放或平移。這樣的地圖不具有快取記憶體的瓦片,被稱為動態地圖服務層。在ArcGISJavaScript API的動態地圖服務為代表的ArcGISDynamicMapServiceLayer。 下面的程式碼行建