遠程通信協(xié)議:從 CORBA 到 gRPC

摘要

  • 一、遠程調用技術簡史
  • 二、gRPC 簡介
  • 三、gRPC 示例代碼

自從產業(yè)界發(fā)明機器聯(lián)網的那一天就已經開始探索最優(yōu)的遠程通信機制焰轻。操作系統(tǒng)如 UNIX、Windows 和 Linux 等都有實現(xiàn)遠程通信的內部協(xié)議昆雀,挑戰(zhàn)在于如何向開發(fā)人員開放一個通信框架辱志。

一、遠程調用技術簡史

在20世紀90年代狞膘,當 TCP/IP 協(xié)議日臻成熟變成網絡通信的黃金標準時揩懒,焦點轉移到跨平臺通信 —— 一臺計算機可以通過某種類型網絡在另一臺計算機上發(fā)起一個動作。例如如 CORBA挽封、DCOM已球、Java RMI 技術,在核心網絡基礎設施之上創(chuàng)造了一個對開發(fā)者友好的抽象層辅愿。這些技術還試圖發(fā)展出一套與開發(fā)語言無關的通信框架智亮,這一點對于客戶機/服務器體系結構至關重要。

隨著本世紀初 Web 技術的演進点待,HTTP 逐漸演變?yōu)槭聦嵣系耐ㄐ艠藴矢胨亍TTP 結合 XML 提供了一種自我描述、不依賴語言亦鳞、與平臺無關的遠程通信框架。這種結合的成果是 SOAP 和 WSDL 標準,它們保證了在各種運行環(huán)境和平臺之間實現(xiàn)互操作的標準化燕差。

下一個沖擊互聯(lián)網的浪潮是 Web 編程遭笋。許多開發(fā)人員發(fā)現(xiàn)定義 SOAP 標準的 HTTP 和 XML 的組合過于嚴格。這時 JavaScript 和 JSON 開始流行了徒探。Web 2.0 現(xiàn)象(API 發(fā)揮了關鍵作用), JSON 替代 XML 成為首選的協(xié)議瓦呼。HTTP 和 JSON 這對致命的組合,催生了一個新的非官方標準 REST 测暗。SOAP 要求嚴格遵守標準和結構定義央串,僅局限于大型企業(yè)應用程序,而 REST 在當代開發(fā)人員中很受歡迎碗啄。

1.1 HTTP, REST 和微服務

歸功于 JavaScript 框架质和,Node.js 以及文檔數(shù)據(jù)庫的發(fā)展,REST 在 Web 開發(fā)者中廣受歡迎稚字。許多應用程序開始基于 REST 實現(xiàn) 饲宿,即使是內部序列化和通信模式領域。但 HTTP 是最有效的消息交換協(xié)議嗎胆描?即使在同一上下文瘫想、同一網絡,或者是同一臺機器上運行的服務之間昌讲?HTTP 的便捷性與高性能之間需要作出權衡国夜,這促使我們回到問題的起點,尋找微服務架構中最優(yōu)的通信框架短绸。

進入 gRPC 時代 —— 來自谷歌车吹,現(xiàn)代的輕量級通信協(xié)議。這是一個高性能的鸠按、開源的通用遠程過程調用(RPC) 框架礼搁,它可以在多種開發(fā)語言、任何操作系統(tǒng)上運行目尖。

gRPC 在推出的第一年內就被 CoreOS馒吴,Netflix,Square 和 Cockroach Labs 等機構采用瑟曲。 CoreOS 團隊的 Etcd饮戳,是一種分布式鍵/值存儲服務,采用 gRPC 實現(xiàn)端通信洞拨。電信公司如 Cisco扯罐,Juniper 和 Arista 都使用 gRPC 實現(xiàn)數(shù)據(jù)流遙測和網絡設備配置。

1.2 什么是 gRPC ?

當我第一次遇到 gRPC烦衣,它使我想到 CORBA歹河。兩個框架都基于語言無關的接口定義語言(IDL) 聲明服務掩浙,通過特定的語言綁定實現(xiàn)。


CORBA 和 gRPC 二者的設計秸歧,都是為了使客戶端相信服務器在同一臺機器厨姚。客戶機在樁(Stub)上調用一個方法(method)键菱,調用過程由底層協(xié)議透明地處理谬墙。

gRPC 的秘訣在于處理序列化的方式。gRPC 基于 Protocol Buffer经备,一個開源的用于結構化數(shù)據(jù)序列化機制拭抬,它是語言和平臺無關的。Protocol Buffer 的描述非常詳細侵蒙,與 XML 類似造虎。但是它們比其他的協(xié)議格式更小,更快蘑志,效率更高累奈。任何需要序列化的自定義數(shù)據(jù)類型在 gRPC 被定義為一個 Protocol Buffer 。

Protocol Buffer 的最新版本是 proto3急但,支持多種開發(fā)語言的代碼生成澎媒,Java , C++,Python波桩,Ruby , Java Lite , JavaScript戒努,Objective-C 和 C # 。當一個 Protocol Buffer 編譯為一個特定的語言镐躲,它的訪問器(setter 和 getter)為每個字段提供定義储玫。

相比于 REST + JSON 組合 ,gRPC 提供更好的性能和安全性萤皂。它極大的促進了在客戶端和服務器之間使用 SSL / TLS 進行身份驗證和數(shù)據(jù)交換加密撒穷。

為什么微服務開發(fā)者需要使用 gRPC ?gRPC 采用 HTTP / 2 以支持高性能的裆熙、可擴展的 API 端礼。報文使用二進制而不是文本通信可以保持載荷緊湊、高效入录。HTTP / 2 請求在一個 TCP 連接上可支持多路復用蛤奥,允許多個消息并發(fā)傳送而不影響網絡資源利用率。gRPC 使用報頭壓縮(header compression )來減少請求和響應的大小僚稿。

二凡桥、gRPC 簡介

2.1 創(chuàng)建 gRPC 服務的流程

  1. 在 Protocol Buffer (.proto) 文件中描述服務和載荷結構
  2. 從 .proto 文件生成 gRPC 代碼
  3. 用一種開發(fā)語言實現(xiàn)服務端
  4. 創(chuàng)建一個客戶端調用服務
  5. 運行服務端和客戶端

Note:Node.js 客戶端不需要生成存根(Stub),只要 Protocol Buffer 文件是可訪問的蚀同,它就可以直接與服務端對話缅刽。

三啊掏、gRPC 示例代碼

為了進一步熟悉 gRPC,我們將用 Python 語言創(chuàng)建一個簡單的計算服務衰猛。它將同時被一個 Python 客戶端和一個 Node.js 客戶端調用脖律。以下測試示例運行在 Mac OS X 。

你可以從 GitHub 庫 https://github.com/grpc/grpc/tree/master/examples 訪問源代碼腕侄,在自己的機器上運行示例。

  • 環(huán)境準備
// 配置 Python gRPC
python -m pip install virtualenv
virtualenv venv
source venv/bin/activate
python -m pip install --upgrade pip

//安裝 gRPC 和 gRPC Tools
python -m pip install grpcio
python -m pip install grpcio-tools

// 配置 Node.js gRPC
npm install grpc --global

//創(chuàng)建目錄
mkdir Proto
mkdir Server
mkdir -p Client/Python
mkdir -p Client/Node
  • 創(chuàng)建 Protocol Buffer 文件
//Proto/Calc.proto
syntax = "proto3";

package calc;

service Calculator {
  rpc Add (AddRequest) returns (AddReply) {}
  rpc Substract (SubstractRequest) returns (SubstractReply) {}
  rpc Multiply (MultiplyRequest) returns (MultiplyReply) {}
  rpc Divide (DivideRequest) returns (DivideReply) {}
}

message AddRequest{
  int32 n1=1;
  int32 n2=2;
}
message AddReply{
  int32 n1=1;
}
message SubstractRequest{
  int32 n1=1;
  int32 n2=2;
}
message SubstractReply{
  int32 n1=1;
}
message MultiplyRequest{
  int32 n1=1;
  int32 n2=2;
}
message MultiplyReply{
  int32 n1=1;
}
message DivideRequest{
  int32 n1=1;
  int32 n2=2;
}
message DivideReply{
  float f1=1;
}
  • 生成 Python 服務端和客戶端代碼
$ python -m grpc.tools.protoc  --python_out=. --grpc_python_out=. --proto_path=. Calc.proto
$ cp Calc_pb2.py ../Server
$ cp Calc_pb2.py ../Client/Python
$ cp Calc.proto ../Client/Node
  • 創(chuàng)建服務端
# Server/Calc_Server.py
from concurrent import futures
import time

import grpc

import Calc_pb2
import Calc_pb2_grpc

_ONE_DAY_IN_SECONDS = 60 * 60 * 24

class Calculator(Calc_pb2.CalculatorServicer):

 def Add(self, request, context):
   return Calc_pb2.AddReply(n1=request.n1+request.n2)

 def Substract(self, request, context):
   return Calc_pb2.SubstractReply(n1=request.n1-request.n2)

 def Multiply(self, request, context):
   return Calc_pb2.MultiplyReply(n1=request.n1*request.n2)

 def Divide(self, request, context):
   return Calc_pb2.DivideReply(f1=request.n1/request.n2)

def serve():
 server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
 Calc_pb2_grpc.add_CalculatorServicer_to_server(Calculator(), server)
 server.add_insecure_port('[::]:50050')
 server.start()

 try:
   while True:
     time.sleep(_ONE_DAY_IN_SECONDS)
 except KeyboardInterrupt:
   server.stop(0)

if __name__ == '__main__':
 serve()
  • 啟動服務端
python Calc_Server.py
  • 創(chuàng)建 Python 客戶端
# Client/Python/Calc_Client.py

from __future__ import print_function

import grpc
import Calc_pb2
import Calc_pb2_grpc

def run():
 channel = grpc.insecure_channel('localhost:50050')
 stub = Calc_pb2_grpc.CalculatorStub(channel)

 response = stub.Add(Calc_pb2.AddRequest(n1=20,n2=10))
 print(response.n1)
 response = stub.Substract(Calc_pb2.SubstractRequest(n1=20,n2=10))
 print(response.n1)
 response = stub.Multiply(Calc_pb2.MultiplyRequest(n1=20,n2=10))
 print(response.n1)
 response = stub.Divide(Calc_pb2.DivideRequest(n1=20,n2=10))
 print(response.f1)

if __name__ == '__main__':
  run()
  • 創(chuàng)建 Node.js 客戶端
//Client/Node/Calc_Client.js
var PROTO_PATH = 'Calc.proto';

var grpc = require('grpc');
var calc_proto = grpc.load(PROTO_PATH).calc;
var params={n1:20, n2:10};

function main() {
 var client = new calc_proto.Calculator('localhost:50050',
                                      grpc.credentials.createInsecure());

 client.divide(params, function(err, response) {
   console.log(response.f1);
 });

 client.multiply(params, function(err, response) {
   console.log(response.n1);
 });

 client.substract(params, function(err, response) {
   console.log(response.n1);
 });

 client.add(params, function(err, response) {
   console.log(response.n1);
 });

}

main();
  • 啟動客戶端 Node.js/Python
$ python Calc_Client.py
30
10
200
2.0

$ node Calc_Client.js
30
10
200
2.0

附表:gRPC 年譜

  • 2011 : Protocol Buffers 2 => language neutral for serializing structured data
  • 2015 : Borg => Large-scale cluster management => Kubernetes
  • 2015 : Stubby => A high performance RPC framework => gRPC
  • July 2016 : Protocol Buffers 3.0.0
  • Aug 2016 : gRPC 1.0 ready for production
  • Sept 2016 : Swift-protobuf
  • Jan 2017 : Grpc Swift
  • Apr 2017 : Google Endpoints => Manage gRPC APIs with Cloud Endpoints
  • Sept 2017 : gRPC 1.6.1
  • Sept 2017 : Protocol Buffers 3.4.1
  • Oct 2017 : Swift-protobuf 1.0
  • Oct 2017 : gRPC 1.7.0
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末芦疏,一起剝皮案震驚了整個濱河市冕杠,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌酸茴,老刑警劉巖分预,帶你破解...
    沈念sama閱讀 206,602評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異薪捍,居然都是意外死亡笼痹,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,442評論 2 382
  • 文/潘曉璐 我一進店門酪穿,熙熙樓的掌柜王于貴愁眉苦臉地迎上來凳干,“玉大人,你說我怎么就攤上這事被济【却停” “怎么了?”我有些...
    開封第一講書人閱讀 152,878評論 0 344
  • 文/不壞的土叔 我叫張陵只磷,是天一觀的道長经磅。 經常有香客問我,道長钮追,這世上最難降的妖魔是什么预厌? 我笑而不...
    開封第一講書人閱讀 55,306評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮元媚,結果婚禮上轧叽,老公的妹妹穿的比我還像新娘。我一直安慰自己惠毁,他們只是感情好犹芹,可當我...
    茶點故事閱讀 64,330評論 5 373
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著鞠绰,像睡著了一般腰埂。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上蜈膨,一...
    開封第一講書人閱讀 49,071評論 1 285
  • 那天屿笼,我揣著相機與錄音牺荠,去河邊找鬼。 笑死驴一,一個胖子當著我的面吹牛休雌,可吹牛的內容都是我干的。 我是一名探鬼主播肝断,決...
    沈念sama閱讀 38,382評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼杈曲,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了胸懈?” 一聲冷哼從身側響起雅宾,我...
    開封第一講書人閱讀 37,006評論 0 259
  • 序言:老撾萬榮一對情侶失蹤脐供,失蹤者是張志新(化名)和其女友劉穎晾浴,沒想到半個月后星岗,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 43,512評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡首有,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,965評論 2 325
  • 正文 我和宋清朗相戀三年燕垃,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片井联。...
    茶點故事閱讀 38,094評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡卜壕,死狀恐怖,靈堂內的尸體忽然破棺而出低矮,到底是詐尸還是另有隱情印叁,我是刑警寧澤,帶...
    沈念sama閱讀 33,732評論 4 323
  • 正文 年R本政府宣布军掂,位于F島的核電站轮蜕,受9級特大地震影響,放射性物質發(fā)生泄漏蝗锥。R本人自食惡果不足惜跃洛,卻給世界環(huán)境...
    茶點故事閱讀 39,283評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望终议。 院中可真熱鬧汇竭,春花似錦、人聲如沸穴张。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,286評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽皂甘。三九已至玻驻,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背璧瞬。 一陣腳步聲響...
    開封第一講書人閱讀 31,512評論 1 262
  • 我被黑心中介騙來泰國打工户辫, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人嗤锉。 一個月前我還...
    沈念sama閱讀 45,536評論 2 354
  • 正文 我出身青樓渔欢,卻偏偏與公主長得像,于是被迫代替她去往敵國和親瘟忱。 傳聞我的和親對象是個殘疾皇子奥额,可洞房花燭夜當晚...
    茶點故事閱讀 42,828評論 2 345

推薦閱讀更多精彩內容