gRPC 簡介實踐

前言

現(xiàn)代的軟件服務(wù)大多數(shù)是分布式應(yīng)用程序茬高,通過暴露自己的 API 對內(nèi)或?qū)ν馓峁┝艘幌盗械墓δ茳c个盆。服務(wù)與服務(wù)之間有時是跨語言矾芙、跨平臺通信的舍沙。

為了解決這些復(fù)雜場景,市面上也涌現(xiàn)了有很多解決方案剔宪。比如構(gòu)建 RESTful 服務(wù)拂铡,將服務(wù)能力轉(zhuǎn)化為資源集合;也有面向函數(shù)調(diào)用的客戶端-服務(wù)器模式:遠程過程調(diào)用(Remote Procedure Calls)葱绒。今天要介紹的 gRPC 就是后者的演變感帅,一個非常受歡迎分布式進程間通信技術(shù)。

認識 gRPC

gRPC 是 Google 在 2015 年推出的 RPC 框架地淀。在認識 gRPC 之前失球,我們先來了解下 RPC 的相關(guān)知識。RPC 主要運用于分布式程序中帮毁,它構(gòu)建了客戶端-服務(wù)器模型实苞,類似于請求-響應(yīng)通信方式,只不過這種請求被我們抽象為了函數(shù)方法 + 入?yún)⑿畔⒘揖危讓拥木W(wǎng)絡(luò)通信則被屏蔽了起來黔牵,到最后就像本地方法調(diào)用一樣。

Protocol Buffers

上面提到了函數(shù)的入?yún)⑿畔⒁危腥雲(yún)⒕陀袑ο箢愋突郑说蕉说膶ο箢愋途蛣荼厣婕暗?br> 網(wǎng)絡(luò)通信過程中的字節(jié)序列化和反序列化過程陆错。在 gRPC 中,采用了 Protobuf(Protocol Buffers)作為序列化和反序列化協(xié)議跃巡。它是一種輕便高效的結(jié)構(gòu)化數(shù)據(jù)存儲格式危号,基于二進制編碼構(gòu)建,能夠減少 CPU 的復(fù)雜解析素邪,保障了 RPC 調(diào)用的高性能。

另外 Protobuf 支持多種編程語言猪半,我們只需要對其進行接口定義描述兔朦,便可以根據(jù)描述文件自動生成客戶端和服務(wù)端需要使用到的結(jié)構(gòu)體和方法函數(shù),就像是代碼自動生成一樣磨确,大大提高了我們的編程效率沽甥。

HTTP/2

gRPC 是基于 HTTP/2 設(shè)計的,HTTP/2 也是 2015 年發(fā)布的乏奥,它是下一代的 HTTP 協(xié)議摆舟,具備很多高級功能,如:

  • 基于二進制格式傳輸邓了,傳輸速度更快恨诱,更緊湊,不易出錯骗炉。
  • 多路復(fù)用照宝,單個連接可發(fā)送多個請求。
  • 對報頭壓縮句葵,能降低傳輸開銷厕鹃。
  • 允許服務(wù)器主動推送。

正是這些 HTTP/2 的特性乍丈,使得 gRPC 能夠使用較少的資源剂碴,獲得較快的響應(yīng),在移動端設(shè)備上更加省電省流量轻专。

gRPC 的使用

接口定義

當我們開發(fā)一個 gRPC 應(yīng)用程序時忆矛,要做的第一件事情就是定義一個接口描述文件。在這個文件里铭若,我們會將函數(shù)洪碳、參數(shù)信息都描述出來,就像下面這個 ProductInfo.proto 文件一樣:

// ProductInfo.proto
syntax = "proto3";
package ecommerce;

// 服務(wù)里的接口列表
service ProductInfo {
    rpc addProduct(Product) returns (ProductID);
    rpc getProduct(ProductID) returns (Product);
}

// 參數(shù)信息
message Product {
    string id = 1;
    string name = 2;
    string description = 3;
}

message ProductID {
    string value = 1;
}

gRPC 服務(wù)端

當我們拿到定義好的接口描述文件 ProductInfo.proto 后叼屠,就可以使用 Protobuf 編譯器:protoc 來生成我們的服務(wù)端代碼了瞳腌。假設(shè)我們的服務(wù)端采用的是 Go 語言,則在經(jīng)過一系列插件的安裝后镜雨,我們就可以使用下面的命令來編譯生成代碼了:

protoc --proto_path=IMPORT_PATH  --go_out=OUT_DIR  --go_opt=paths=source_relative path/to/file.proto

protoc 支持多種語言嫂侍,具體大伙可以按照官方提供的來生成代碼,總之,我們將接口定義文件挑宠,轉(zhuǎn)化為了我們需要的服務(wù)端代碼菲盾,我們只需要引用并實現(xiàn)接口函數(shù)即可為客戶端提供對應(yīng)的服務(wù)了:

import (
  ...
  "context"
  pb "github.com/grpc-up-and-running/samples/ch02/productinfo/go/proto"
  "google.golang.org/grpc"
  ...
)

// ProductInfo 接口的實現(xiàn)

// Add product remote method
func (s *server) AddProduct(ctx context.Context, in *pb.Product) (
      *pb.ProductID, error) {
   // 具體的業(yè)務(wù)邏輯
}

// Get product remote method
func (s *server) GetProduct(ctx context.Context, in *pb.ProductID) (
     *pb.Product, error) {
   // 具體的業(yè)務(wù)邏輯
}

當然,定義完后還得注冊下服務(wù):

func main() {
  lis, _ := net.Listen("tcp", port)
  s := grpc.NewServer()
  pb.RegisterProductInfoServer(s, &server{})
  if err := s.Serve(lis); err != nil {
    log.Fatalf("failed to serve: %v", err)
  }
}

gRPC 客戶端

與服務(wù)端類似各淀,我們將根據(jù) ProductInfo.proto 文件生成客戶端的代碼懒鉴,即所謂的存根(Stub)。假設(shè)我們的客戶端使用的是 Java 語言碎浇,則我們可以像下面一樣使用 Stub 了:

// 根據(jù)服務(wù)地址創(chuàng)建通道
ManagedChannel channel = ManagedChannelBuilder.forAddress("localhost", 8080)
   .usePlaintext(true)
   .build();

// 根據(jù)通道創(chuàng)建存根
ProductInfoGrpc.ProductInfoBlockingStub stub =
       ProductInfoGrpc.newBlockingStub(channel);

// 調(diào)用存根里的方法
StringValue productID = stub.addProduct(
       Product.newBuilder()
       .setName("Apple iPhone 11")
       .setDescription("Meet Apple iPhone 11." +
            "All-new dual-camera system with " +
            "Ultra Wide and Night mode.")
       .build());
       

整體流程

可以看到临谱,其實接口定義文件 ProductInfo.proto 就是一個粘合劑,它將服務(wù)端的邏輯抽象成了語言無關(guān)的描述文件奴璃,讓服務(wù)端依次去實現(xiàn)具體邏輯悉默。然后通過它生成的客戶端存根(Stub)則屏蔽了底層的通信流程,只需要暴露讓上層可以調(diào)用的函數(shù)即可苟穆,就像本地函數(shù)調(diào)用一樣抄课。

gRPC 流程

其他特性

Metadata(元數(shù)據(jù))

gRPC 沒有使用 HTTP 所謂的 header 字段,而是使用 Metadata 來構(gòu)建相關(guān)的頭部信息雳旅。Metadata 是在一次 RPC 調(diào)用過程中關(guān)于這次調(diào)用的信息跟磨,以 key-value 的形式存在。

其中 key 是 string 類型岭辣, value 也是一組 string吱晒。 Metadata 對于 gRPC 本身來說透明,它使得 client 和 server 能為對方提供本次調(diào)用的信息沦童。就像一次 http 請求的 RequestHeader 和 ResponseHeader仑濒,http header 的生命周期是一次 http 請求, Metadata 的生命周期則是一次 RPC 調(diào)用偷遗。

Streaming(流)

得意于 HTTP/2 的多路復(fù)用能力墩瞳,使得 gRPC 的客戶端和服務(wù)端能夠進行流式傳輸,例如我們可以邊傳輸氏豌,邊處理數(shù)據(jù)喉酌。 gRPC 的流式傳輸主要分為了下面幾種:

  • 服務(wù)端流式 RPC:客戶端發(fā)送單個請求,服務(wù)器可以發(fā)回多個響應(yīng)泵喘。
  • 客戶端流式 RPC:客戶端發(fā)送多個請求泪电,而服務(wù)器只發(fā)回一個響應(yīng)。
  • 雙向流式 RPC: 客戶端和服務(wù)器同時相互發(fā)送消息而不等待響應(yīng)纪铺。

Interceptors(攔截器)

gRPC 支持在請求/響應(yīng)中使用攔截功能相速,進行消息的攔截并修改它們,這跟平常我們提到的 HTTP 中間件非常的相似鲜锚⊥晃埽基于攔截器苫拍,我們可以實現(xiàn)類似身份認證、鏈式調(diào)用旺隙、重試等功能绒极,這些對應(yīng)微服務(wù)是非常的契合的。

負載均衡

gRPC 官方提供了關(guān)于負載均衡的方案:Load Balancing in gRPC蔬捷,有興趣的同學(xué)可以自己研究下垄提。

gRPC 優(yōu)點

正是前面的 Protobuf 和 HTTP/2 的底層支持,使得 gRPC 在一推出后就受到了許多人的追捧抠刺,它主要有以下幾個特點:

  • 性能:比 REST + JSON 快 10 倍的性能塔淤,消息負載小而緊湊,并且通過壓縮加快了傳輸速度速妖。
  • 代碼生成:通過語言無關(guān)的接口定義文件快速的生成各種語言的客戶端、服務(wù)端代碼聪黎,提高了開發(fā)效率罕容。
  • 可拓展性好:提高了一體化的 RPC 解決方案,有許多內(nèi)置的解決方案稿饰,例如數(shù)據(jù)交換锦秒、加密、身份驗證喉镰、超時取消旅择、攔截器、負載均衡等侣姆。

gRPC 的缺點

與其他技術(shù)一樣生真,gRPC 有優(yōu)點也有缺點,下面就是我們在開發(fā)應(yīng)用程序時需要注意的點:

  • 有限的瀏覽器支持:由于 gRPC 大量使用 HTTP/2捺宗,因此無法之間在 Web 瀏覽器調(diào)用 gRPC 服務(wù)柱蟀,gRPC 比較適用于手機 APP Client。
  • 不友好格式:Protobuf 將 gRPC 消息壓縮成非可讀格式蚜厉,需要反序列化才拿到消息格式长已,不好調(diào)試。

總結(jié)

現(xiàn)代軟件應(yīng)用程序已經(jīng)很少孤立存在了昼牛,更多是通過網(wǎng)絡(luò)通信進行服務(wù)溝通术瓮。gRPC 是一種可拓展、松耦合且類型安全的解決方案.

與傳統(tǒng)的基于 REST/HTTP 的通信相比贰健,它能進行更有效的進程間通信胞四,特別是現(xiàn)在流行的微服務(wù)架構(gòu)里,在很多框架里都能看到它的身影存在霎烙。所以撬讽,是時候開始試試它的魅力所在了蕊连!

參考

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市游昼,隨后出現(xiàn)的幾起案子甘苍,更是在濱河造成了極大的恐慌,老刑警劉巖烘豌,帶你破解...
    沈念sama閱讀 216,591評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件载庭,死亡現(xiàn)場離奇詭異,居然都是意外死亡廊佩,警方通過查閱死者的電腦和手機囚聚,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來标锄,“玉大人顽铸,你說我怎么就攤上這事×匣剩” “怎么了谓松?”我有些...
    開封第一講書人閱讀 162,823評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長践剂。 經(jīng)常有香客問我鬼譬,道長,這世上最難降的妖魔是什么逊脯? 我笑而不...
    開封第一講書人閱讀 58,204評論 1 292
  • 正文 為了忘掉前任优质,我火速辦了婚禮,結(jié)果婚禮上军洼,老公的妹妹穿的比我還像新娘巩螃。我一直安慰自己,他們只是感情好歉眷,可當我...
    茶點故事閱讀 67,228評論 6 388
  • 文/花漫 我一把揭開白布牺六。 她就那樣靜靜地躺著,像睡著了一般汗捡。 火紅的嫁衣襯著肌膚如雪淑际。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,190評論 1 299
  • 那天扇住,我揣著相機與錄音春缕,去河邊找鬼。 笑死艘蹋,一個胖子當著我的面吹牛锄贼,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播女阀,決...
    沈念sama閱讀 40,078評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼宅荤,長吁一口氣:“原來是場噩夢啊……” “哼屑迂!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起冯键,我...
    開封第一講書人閱讀 38,923評論 0 274
  • 序言:老撾萬榮一對情侶失蹤惹盼,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后惫确,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體手报,經(jīng)...
    沈念sama閱讀 45,334評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,550評論 2 333
  • 正文 我和宋清朗相戀三年改化,在試婚紗的時候發(fā)現(xiàn)自己被綠了掩蛤。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,727評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡陈肛,死狀恐怖揍鸟,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情句旱,我是刑警寧澤蜈亩,帶...
    沈念sama閱讀 35,428評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站前翎,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏畅涂。R本人自食惡果不足惜港华,卻給世界環(huán)境...
    茶點故事閱讀 41,022評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望午衰。 院中可真熱鬧立宜,春花似錦、人聲如沸臊岸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,672評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽帅戒。三九已至灯帮,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間逻住,已是汗流浹背钟哥。 一陣腳步聲響...
    開封第一講書人閱讀 32,826評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留瞎访,地道東北人腻贰。 一個月前我還...
    沈念sama閱讀 47,734評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像扒秸,于是被迫代替她去往敵國和親播演。 傳聞我的和親對象是個殘疾皇子冀瓦,可洞房花燭夜當晚...
    茶點故事閱讀 44,619評論 2 354

推薦閱讀更多精彩內(nèi)容

  • 基礎(chǔ):GRPC的產(chǎn)生動機和設(shè)計原則grpc| python 實戰(zhàn) grpcPython版gRPC入門實驗 - 知乎...
    王勝廣閱讀 3,916評論 0 2
  • 1.簡介 1.1 問題 目前程序開發(fā)中,一個程序基本上是以各個服務(wù)組成写烤,例如一個簡單的系統(tǒng)翼闽,用戶發(fā)起rest請求,...
    禿頭猿猿閱讀 2,737評論 0 3
  • RPC(Remote Procedure Call) RPC(Remote Procedure Call)— 遠程...
    wavesnow閱讀 34,515評論 0 4
  • 相信大家都聽過RPC顶霞、HTTP肄程、Socket等協(xié)議,他們均可用于業(yè)務(wù)中來進行數(shù)據(jù)通信选浑,又根據(jù)各自協(xié)議的特點蓝厌,應(yīng)用場...
    Reaburoa閱讀 745評論 0 1
  • 我們將介紹 Go 語言中最流行的 RPC 框架:gRPC,并帶你探索其相對應(yīng)的技術(shù)棧古徒。 首先我們需要對RPC有所了...
    代碼小雜魚閱讀 632評論 0 0