摘要
- 一、遠程調用技術簡史
- 二、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 服務的流程
- 在 Protocol Buffer (.proto) 文件中描述服務和載荷結構
- 從 .proto 文件生成 gRPC 代碼
- 用一種開發(fā)語言實現(xiàn)服務端
- 創(chuàng)建一個客戶端調用服務
- 運行服務端和客戶端
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