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

摘要

  • 一、遠(yuǎn)程調(diào)用技術(shù)簡(jiǎn)史
  • 二、gRPC 簡(jiǎn)介
  • 三、gRPC 示例代碼

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

一、遠(yuǎn)程調(diào)用技術(shù)簡(jiǎn)史

在20世紀(jì)90年代,當(dāng) TCP/IP 協(xié)議日臻成熟變成網(wǎng)絡(luò)通信的黃金標(biāo)準(zhǔn)時(shí),焦點(diǎn)轉(zhuǎn)移到跨平臺(tái)通信 —— 一臺(tái)計(jì)算機(jī)可以通過某種類型網(wǎng)絡(luò)在另一臺(tái)計(jì)算機(jī)上發(fā)起一個(gè)動(dòng)作。例如如 CORBA、DCOM、Java RMI 技術(shù),在核心網(wǎng)絡(luò)基礎(chǔ)設(shè)施之上創(chuàng)造了一個(gè)對(duì)開發(fā)者友好的抽象層。這些技術(shù)還試圖發(fā)展出一套與開發(fā)語(yǔ)言無關(guān)的通信框架,這一點(diǎn)對(duì)于客戶機(jī)/服務(wù)器體系結(jié)構(gòu)至關(guān)重要。

隨著本世紀(jì)初 Web 技術(shù)的演進(jìn),HTTP 逐漸演變?yōu)槭聦?shí)上的通信標(biāo)準(zhǔn)。HTTP 結(jié)合 XML 提供了一種自我描述、不依賴語(yǔ)言、與平臺(tái)無關(guān)的遠(yuǎn)程通信框架。這種結(jié)合的成果是 SOAP 和 WSDL 標(biāo)準(zhǔn),它們保證了在各種運(yùn)行環(huán)境和平臺(tái)之間實(shí)現(xiàn)互操作的標(biāo)準(zhǔn)化。

下一個(gè)沖擊互聯(lián)網(wǎng)的浪潮是 Web 編程。許多開發(fā)人員發(fā)現(xiàn)定義 SOAP 標(biāo)準(zhǔn)的 HTTP 和 XML 的組合過于嚴(yán)格。這時(shí) JavaScript 和 JSON 開始流行了。Web 2.0 現(xiàn)象(API 發(fā)揮了關(guān)鍵作用), JSON 替代 XML 成為首選的協(xié)議。HTTP 和 JSON 這對(duì)致命的組合,催生了一個(gè)新的非官方標(biāo)準(zhǔn) REST 。SOAP 要求嚴(yán)格遵守標(biāo)準(zhǔn)和結(jié)構(gòu)定義,僅局限于大型企業(yè)應(yīng)用程序,而 REST 在當(dāng)代開發(fā)人員中很受歡迎。

1.1 HTTP, REST 和微服務(wù)

歸功于 JavaScript 框架,Node.js 以及文檔數(shù)據(jù)庫(kù)的發(fā)展,REST 在 Web 開發(fā)者中廣受歡迎。許多應(yīng)用程序開始基于 REST 實(shí)現(xiàn) ,即使是內(nèi)部序列化和通信模式領(lǐng)域。但 HTTP 是最有效的消息交換協(xié)議嗎?即使在同一上下文、同一網(wǎng)絡(luò),或者是同一臺(tái)機(jī)器上運(yùn)行的服務(wù)之間?HTTP 的便捷性與高性能之間需要作出權(quán)衡,這促使我們回到問題的起點(diǎn),尋找微服務(wù)架構(gòu)中最優(yōu)的通信框架。

進(jìn)入 gRPC 時(shí)代 —— 來自谷歌,現(xiàn)代的輕量級(jí)通信協(xié)議。這是一個(gè)高性能的、開源的通用遠(yuǎn)程過程調(diào)用(RPC) 框架,它可以在多種開發(fā)語(yǔ)言、任何操作系統(tǒng)上運(yùn)行。

gRPC 在推出的第一年內(nèi)就被 CoreOS,Netflix,Square 和 Cockroach Labs 等機(jī)構(gòu)采用。 CoreOS 團(tuán)隊(duì)的 Etcd,是一種分布式鍵/值存儲(chǔ)服務(wù),采用 gRPC 實(shí)現(xiàn)端通信。電信公司如 Cisco,Juniper 和 Arista 都使用 gRPC 實(shí)現(xiàn)數(shù)據(jù)流遙測(cè)和網(wǎng)絡(luò)設(shè)備配置。

1.2 什么是 gRPC ?

當(dāng)我第一次遇到 gRPC,它使我想到 CORBA。兩個(gè)框架都基于語(yǔ)言無關(guān)的接口定義語(yǔ)言(IDL) 聲明服務(wù),通過特定的語(yǔ)言綁定實(shí)現(xiàn)。


CORBA 和 gRPC 二者的設(shè)計(jì),都是為了使客戶端相信服務(wù)器在同一臺(tái)機(jī)器??蛻魴C(jī)在樁(Stub)上調(diào)用一個(gè)方法(method),調(diào)用過程由底層協(xié)議透明地處理。

gRPC 的秘訣在于處理序列化的方式。gRPC 基于 Protocol Buffer,一個(gè)開源的用于結(jié)構(gòu)化數(shù)據(jù)序列化機(jī)制,它是語(yǔ)言和平臺(tái)無關(guān)的。Protocol Buffer 的描述非常詳細(xì),與 XML 類似。但是它們比其他的協(xié)議格式更小,更快,效率更高。任何需要序列化的自定義數(shù)據(jù)類型在 gRPC 被定義為一個(gè) Protocol Buffer 。

Protocol Buffer 的最新版本是 proto3,支持多種開發(fā)語(yǔ)言的代碼生成,Java , C++,Python,Ruby , Java Lite , JavaScript,Objective-C 和 C # 。當(dāng)一個(gè) Protocol Buffer 編譯為一個(gè)特定的語(yǔ)言,它的訪問器(setter 和 getter)為每個(gè)字段提供定義。

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

為什么微服務(wù)開發(fā)者需要使用 gRPC ?gRPC 采用 HTTP / 2 以支持高性能的、可擴(kuò)展的 API 。報(bào)文使用二進(jìn)制而不是文本通信可以保持載荷緊湊、高效。HTTP / 2 請(qǐng)求在一個(gè) TCP 連接上可支持多路復(fù)用,允許多個(gè)消息并發(fā)傳送而不影響網(wǎng)絡(luò)資源利用率。gRPC 使用報(bào)頭壓縮(header compression )來減少請(qǐng)求和響應(yīng)的大小。

二、gRPC 簡(jiǎn)介

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

  1. 在 Protocol Buffer (.proto) 文件中描述服務(wù)和載荷結(jié)構(gòu)
  2. 從 .proto 文件生成 gRPC 代碼
  3. 用一種開發(fā)語(yǔ)言實(shí)現(xiàn)服務(wù)端
  4. 創(chuàng)建一個(gè)客戶端調(diào)用服務(wù)
  5. 運(yùn)行服務(wù)端和客戶端

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

三、gRPC 示例代碼

為了進(jìn)一步熟悉 gRPC,我們將用 Python 語(yǔ)言創(chuàng)建一個(gè)簡(jiǎn)單的計(jì)算服務(wù)。它將同時(shí)被一個(gè) Python 客戶端和一個(gè) Node.js 客戶端調(diào)用。以下測(cè)試示例運(yùn)行在 Mac OS X 。

你可以從 GitHub 庫(kù) https://github.com/grpc/grpc/tree/master/examples 訪問源代碼,在自己的機(jī)器上運(yùn)行示例。

  • 環(huán)境準(zhǔ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 服務(wù)端和客戶端代碼
$ 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)建服務(wù)端
# 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()
  • 啟動(dòng)服務(wù)端
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();
  • 啟動(dòng)客戶端 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
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容