天天看點

揭開gRPC神秘面紗

一、什麼是RPC

RPC(Remote Procedure Call):

遠端過程調用,它是一種通過網絡從遠端計算機程式上請求服務,而不需要了解底層網絡技術的思想。

RPC 是一種技術思想而非一種規範或協定,常見 RPC 技術和架構有:

應用級的服務架構:阿裡的 Dubbo/Dubbox、Google gRPC、Spring Boot/Spring Cloud。

遠端通信協定:RMI、Socket、SOAP(HTTP XML)、REST(HTTP JSON)。

通信架構:MINA 和 Netty。

目前流行的開源 RPC 架構還是比較多的,有阿裡巴巴的 Dubbo、Facebook 的 Thrift、Google 的 gRPC、Twitter 的 Finagle 等。

揭開gRPC神秘面紗

RPC 的核心功能是指實作一個 RPC 最重要的功能子產品,就是上圖中的”RPC 協定”部分:

一個 RPC 的核心功能主要有 5 個部分組成,分别是:

  • 用戶端
  • 用戶端 Stub
  • 網絡傳輸子產品
  • 服務端 Stub
  • 服務端
    揭開gRPC神秘面紗

下面分别介紹核心 RPC 架構的重要組成:

  • 用戶端(Client):服務調用方。
  • 用戶端存根(Client Stub):存放服務端位址資訊,将用戶端的請求參數資料資訊打包成網絡消息,再通過網絡傳輸發送給服務端。
  • 服務端存根(Server Stub):接收用戶端發送過來的請求消息并進行解包,然後再調用本地服務進行處理。
  • 服務端(Server):服務的真正提供者。
  • Network Service:底層傳輸,可以是 TCP 或 HTTP。

百度百科上查找的資料

什麼是代理和存根

打個比方,你到自動取款機上去取款;你就是客戶,取款機就是你的代理;你不會在乎錢具體放在那裡,你隻想看到足夠或更多的錢從出口出來(這就是com的透明性)。你同銀行之間的操作完全是取款機代理實作。 你的取款請求通過取款機,傳到另一頭,銀行的伺服器,他也沒有必要知道你在哪兒取錢,他所關心的是你的身份,和你取款多少。當他确認你的權限,就進行相應的操作,傳回操作結果給取款機,取款機根據伺服器傳回結果,從保險櫃裡取出相應數量的錢給你。你取出卡後,操作完成。 取款機不是直接同伺服器連接配接的,他們之間還有一個“存根”,取款機與存根通信,伺服器與存根通信。從某種意義上說存根就是伺服器的代理。

按照我自己通俗的了解,用戶端與stub存根進行資料互動等價與直接與服務端通訊,對于接口調用相當于本地調用。

無非就是用戶端将要調用服務端的接口和參數傳遞給client stub,client stub将上述資訊序列化為能在網絡上傳輸的資料,這一點應用完全沒必要清楚細節。傳輸到server stub再将資料反序列化成實際調用接口的資訊。server stub根據傳輸過來的資訊調用服務端提供的服務接口,再将傳回值通過stub傳輸給用戶端。

簡單歸納與普通的C/S通訊,僅僅多了一個中間件stub。

揭開gRPC神秘面紗

RPC 的核心功能主要由 5 個子產品組成,如果想要自己實作一個 RPC,最簡單的方式要實作三個技術點,分别是:

  • 服務尋址
  • 資料流的序列化和反序列化
  • 網絡傳輸

在遠端過程調用時,用戶端跟服務端是不同的程序,不能通過記憶體來傳遞參數。

這時候就需要用戶端把參數先轉成一個位元組流,傳給服務端後,再把位元組流轉成自己能讀取的格式。

隻有二進制資料才能在網絡中傳輸,序列化和反序列化的定義是:

  • 将對象轉換成二進制流的過程叫做序列化
  • 将二進制流轉換成對象的過程叫做反序列化

這個過程叫序列化和反序列化。同理,從服務端傳回的值也需要序列化反序列化的過程。

通俗的了解:

現在我們都會在淘寶上買桌子,桌子這種很不規則不東西,該怎麼從一個城市運輸到另一個城市,這時候一般都會把它拆掉成闆子,再裝到箱子裡面,就可以快遞寄出去了,這個過程就類似我們的序列化的過程(把資料轉化為可以存儲或者傳輸的形式)。當買家收到貨後,就需要自己把這些闆子組裝成桌子的樣子,這個過程就像反序列 的過程(轉化成當初的資料對象)。

詳細内容參考該文章。

花了一個星期,我終于把RPC架構整明白了! - 51CTO.COM

二、什麼是gRPC

在gRPC中,用戶端應用程式可以直接在其他計算機上的伺服器應用程式上調用方法,就好像它是本地對象一樣,進而使您更輕松地建立分布式應用程式和服務。與許多RPC系統一樣,gRPC圍繞定義服​​務的思想,指定可通過其參數和傳回類型遠端調用的方法。在伺服器端,伺服器實作此接口并運作gRPC伺服器以處理用戶端調用。在用戶端,用戶端具有一個存根(在某些語言中僅稱為用戶端),提供與伺服器相同的方法。

存根通俗了解為“開發票一式兩份”,作為服務端方法的鏡像或者說映射。簡單說用戶端執行存根的方法,等價于調用服務端的方法。

揭開gRPC神秘面紗

從Google内部的伺服器到您自己的桌上型電腦,gRPC用戶端和伺服器可以在各種環境中運作并互相通信,并且可以使用gRPC支援的任何語言編寫。是以,例如,您可以使用Go,Python或Ruby中的用戶端輕松地用Java建立gRPC伺服器。此外,最新的Google API的接口将具有gRPC版本,可讓您輕松地在應用程式中建構Google功能。基于HTTP2.0的傳輸協定,高效多語言支援。

(簡單的了解就是遠端用戶端可以直接調用服務端的接口。)

三、什麼是Protobuf

1.概要

protobuf(Google Protocol Buffers)是Google提供一個具有高效的協定資料交換格式工具庫。可用于通訊協定、資料存儲等領域的語言無關、平台無關、可擴充的序列化結構資料格式。

grpc作為rpc的一種,肯定有服務間的網絡調用,調用就一定會有資料的傳輸,而這個資料的傳輸就用的是protobuf去序列化和反序列化的,一個請求的内容在調用方序列化通過網絡傳送到服務方,服務方就能用protobuf反序列化出來。

優勢:

  • 平台無關,語言無關,可擴充;
  • 提供了友好的動态庫,使用簡單;
  • 解析速度快,比對應的XML快約20-100倍;
  • 序列化資料非常簡潔、緊湊,與XML相比,其序列化之後的資料量約為1/3到1/10。
揭開gRPC神秘面紗

幹貨|了解Google遠端過程調用(gRPC)技術

2.舉例說明

helloworld.proto

syntax = "proto3";

option java_multiple_files = true;
option java_package = "io.grpc.examples.helloworld";
option java_outer_classname = "HelloWorldProto";
option objc_class_prefix = "HLW";

package helloworld;

// The greeting service definition.
service Greeter {
  // Sends a greeting
  rpc SayHello (HelloRequest) returns (HelloReply) {}
}

// The request message containing the user's name.
message HelloRequest {
  string name = 1;
}

// The response message containing the greetings
message HelloReply {
  string message = 1;
}
           

說明:

(1)syntax = “proto3”

表示使用的protobuf編譯器版本為v3

(2)option中的幾個選項說明

選項不對message的定義産生任何的效果,隻會在一些特定的場景中起到作用,下面是一部分例子,完整的選項清單可以前往google/protobuf/descriptor.proto檢視

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

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

java_multiple_files :

option java_multiple_files = true; 該選項為true時,生成的Java類将是包級别的,否則會在一個包裝類中。

java_package :

這個選項表明生成java類所在的包。如果在.proto檔案中沒有明确的聲明java_package,就采用預設的包名。當然了,預設方式産生的 java包名并不是最好的方式,按照應用名稱倒序方式進行排序的。如果不需要産生java代碼,則該選項将不起任何作用。如:

option java_package = “com.example.foo”;

java_outer_classname ):

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

option java_outer_classname = “Ponycopter”;

objc_class_prefix:

設定Objective-C類的字首,添加到所有Objective-C從此.proto檔案産生的類和枚舉類型。沒有預設值,所使用的字首應該是蘋果推薦的3-5個大寫字元,注意2個位元組的字首是蘋果所保留的。

其他字段詳細參考部落格 。

(3)package helloworld

聲明了一個包名,用來防止不同的消息類型命名沖突,類似于 namespace命名空間。

(4)定義service

定義一個SayHello的rpc方法,helloworld定義的是一個簡單的RPC,用戶端使用存根發送請求到伺服器并等待響應傳回,就像平常的函數調用一樣。

rpc SayHello (HelloRequest) returns (HelloReply) {}
           

gRPC 允許你定義四類服務方法:

單項 RPC,即用戶端發送一個請求給服務端,從服務端擷取一個應答,就像一次普通的函數調用。

rpc SayHello(HelloRequest) returns (HelloResponse){
}
           

服務端流式 RPC,即用戶端發送一個請求給服務端,可擷取一個資料流用來讀取一系列消息。用戶端從傳回的資料流裡一直讀取直到沒有更多消息為止。

rpc LotsOfReplies(HelloRequest) returns (stream HelloResponse){
}
           

用戶端流式 RPC,即用戶端用提供的一個資料流寫入并發送一系列消息給服務端。一旦用戶端完成消息寫入,就等待服務端讀取這些消息并傳回應答。

rpc LotsOfGreetings(stream HelloRequest) returns (HelloResponse) {
}
           

雙向流式 RPC,即兩邊都可以分别通過一個讀寫資料流來發送一系列消息。這兩個資料流操作是互相獨立的,是以用戶端和服務端能按其希望的任意順序讀寫,例如:服務端可以在寫應答前等待所有的用戶端消息,或者它可以先讀一個消息再寫一個消息,或者是讀寫相結合的其他方式。每個資料流裡消息的順序會被保持。

rpc BidiHello(stream HelloRequest) returns (stream HelloResponse){
}
           

深入了解流,什麼是流?

grpc将根據proto雙方協商好的資料類型,序列化為二進制進行傳輸。之後再反序列化恢複資料原樣。

(5)定義消息類型

指定字段類型

HelloRequest 消息包含一個string類型。

配置設定标示号

在消息定義中,每個字段都有唯一的一個數字辨別符。這些辨別符是用來在消息的二進制格式中識别各個字段的,一旦開始使用就不能夠再改變。

注:[1,15]之内的辨別号在編碼的時候會占用一個位元組。[16,2047]之内的辨別号則占用2個位元組。是以應該為那些頻繁出現的消息元素保留 [1,15]之内的辨別号。

切記:要為将來有可能添加的、頻繁出現的辨別号預留一些辨別号。

最小的辨別号可以從1開始,最大到2^29 - 1, or 536,870,911。不可以使用其中的[19000-19999]( (從FieldDescriptor::kFirstReservedNumber 到 FieldDescriptor::kLastReservedNumber))的辨別号, Protobuf協定實作中對這些進行了預留。如果非要在.proto檔案中使用這些預留辨別号,編譯時就會報警。同樣你也不能使用早期保留的辨別号。

預設值

當一個消息被解析的時候,如果被編碼的資訊不包含一個特定的singular元素,被解析的對象鎖對應的域被設定位一個預設值,對于不同類型指定如下:

對于strings,預設是一個空string

對于bytes,預設是一個空的bytes

對于bools,預設是false

對于數值類型,預設是0

對于枚舉,預設是第一個定義的枚舉值,必須為0;

Protobuf3文法詳解 - 望星辰大海 - 部落格園

3.protoc是什麼?

protoc是proto檔案的編譯器,目前可以将proto檔案編譯成C++、Java、Python三種代碼檔案,編譯格式如下:

protoc -I=$SRC_DIR --cpp_out=$DST_DIR /path/to/file.proto
           

上面的指令會生成xxx.pb.h 和 xxx.pb.cc兩個C++檔案。

個人測試

測試指令

protoc -I=/home/workspace/test/grpc_test --cpp_out=/home/workspace/test/grpc_test/out /home/workspace/test/grpc_test/helloworld.proto
           
揭開gRPC神秘面紗

proto檔案的作用?

定義我們程式中需要處理的結構化資料。message為結構化資料。簡單了解為結構體,裡面記錄要存儲的資料類型。

編譯 .proto 檔案的作用是什麼?

寫好 proto 檔案之後就可以用 Protobuf 編譯器将該檔案編譯成目智語言了。

helloworld.pb.h , 定義了 C++ 類的頭檔案

helloworld.pb.cc , C++ 類的實作檔案

在生成的頭檔案中,定義了一個 C++ 類 helloworld,

四、參考資料

序列化和反序列化了解 - 簡

花了一個星期,我終于把RPC架構整明白了! - 51CTO.COM

gRPC –指南

gRPC 官方文檔中文版_V1.0

Protobuf學習 - 入門 - Aut - 部落格園

幹貨|了解Google遠端過程調用(gRPC)技術,這一篇就夠了