一、什么是PRC ?
1.1 定義
RPC(Remote Procedure Call),遠程過程調用。是一個計算機通信協議,該協議允許運行於一台計算機的程序調用另一個地址空間(通常為另一台開放網絡的計算機)的子程序。而在開發人員的視角看來,該調用過程和調用本地過程一樣,無需額外編程。在這個過程中,RPC也可以輕易的進行跨語言調用。
舉例來說:存在兩台服務器A,B,在A服務器上部署了一個應用程序,該應用程序需要調用在B服務器上應用提供的函數/方法。RPC的作用,就是讓A可以通過網絡跨內存空間調用B上的函數/方法。
為什么不用Restful接口要提出RPC? Restful接口每次調用時,都需要編寫http請求。RPC能做到的,是像本地調用一樣去發起遠程調用而讓使用者對遠程調用無感知。
1.2 組件
-
Server:RPC服務的提供者,上述例子中為服務器B。
-
Client:RPC服務的調用方,上述例子中的服務器A。
-
Client Stub:存放Server的地址信息,再將服務端的請求打包成網絡消息,通過網絡遠程發送給服務方。
-
Server stub:接收客戶端發送過來的消息,將消息解包,並調用本地的方法。
1.3 RPC調用過程

(1) 客戶端(client)以本地調用方式(即以接口的方式)調用服務;
(2) 客戶端存根(client stub)接收到調用后,負責將方法、參數等組裝成能夠進行網絡傳輸的消息體(將消息體對象序列化為二進制);
(3) 客戶端通過sockets將消息發送到服務端;
(4) 服務端存根( server stub)收到消息后進行解碼(將消息對象反序列化);
(5) 服務端存根( server stub)根據解碼結果調用本地的服務;
(6) 本地服務執行並將結果返回給服務端存根( server stub);
(7) 服務端存根( server stub)將返回結果打包成消息(將結果消息對象序列化);
(8) 服務端(server)通過sockets將消息發送到客戶端;
(9) 客戶端存根(client stub)接收到結果消息,並進行解碼(將結果消息發序列化);
(10) 客戶端(client)得到最終結果。
RPC將中間過程封裝起來,使用戶無感知。
二、什么是GRPC?
gRPC (Google Remote Procedure Calls) 是Google發起的一個開源遠程過程調用 (Remote procedure call) 系統。該系統基於 HTTP/2 協議傳輸,使用Protocol Buffers 作為接口描述語言。
gRPC是RPC系統的一種。 其基於 HTTP/2 標准設計,帶來諸如雙向流、流控、頭部壓縮、單 TCP 連接上的多復用請求等特性。
2.1 Protocol Buffers
Protocol Buffers是一種序列化數據結構的協議。其包含一個接口描述語言用來描述一些方法與數據結構,並可以通過一些工具來將這些描述轉化為代碼。具體的Protocol語法這里不談,接下來用一個Python gRPC例子說明gRPC基礎概念。
2.2 gRPC Python示例 (參考:daydaygo)
通過代碼往往能加深理解!
環境需求:Python3、Proto3、pip包:grpcio、grpc-tools。
- 編寫
.proto文件:定義服務Greeter和 APISayHello。定義請求和回復message格式。
// helloworld.proto
syntax = "proto3";
service Greeter {
rpc SayHello(HelloRequest) returns (HelloReply) {}
}
message HelloRequest {
string name = 1;
}
message HelloReply {
string message = 1;
}
- 通過grpc-tools編譯protocol文件
python -m grpc_tools.protoc --python_out=. --grpc_python_out=. -I. helloworld.proto
python -m grpc_tools.protoc: python 下的 protoc 編譯器通過 python 模塊(module) 實現, 所以說這一步非常省心--python_out=.: 編譯生成處理 protobuf 相關的代碼的路徑, 這里生成到當前目錄--grpc_python_out=.: 編譯生成處理 grpc 相關的代碼的路徑, 這里生成到當前目錄-I. helloworld.proto: proto 文件的路徑, 這里的 proto 文件在當前目錄
編譯后生成兩個Python模塊
helloworld_pb2.py: 用來和 protobuf 數據進行交互
helloworld_pb2_grpc.py: 用來和 grpc 進行交互
- gRPC Server實現
from concurrent import futures
import time
import grpc
import helloworld_pb2
import helloworld_pb2_grpc
# 實現 proto 文件中定義的 GreeterServicer
class Greeter(helloworld_pb2_grpc.GreeterServicer):
# 實現 proto 文件中定義的 rpc 調用
def SayHello(self, request, context):
return helloworld_pb2.HelloReply(message = 'hello {msg}'.format(msg = request.name))
def serve():
# 注冊啟動 rpc 服務
server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
helloworld_pb2_grpc.add_GreeterServicer_to_server(Greeter(), server)
server.add_insecure_port('[::]:50051')
server.start()
try:
while True:
time.sleep(60*60*24) # one day in seconds
except KeyboardInterrupt:
server.stop(0)
if __name__ == '__main__':
serve()
server端提供了 rpc服務的實現並注冊開啟rpc服務。
- gRPC Client實現
import grpc
import helloworld_pb2
import helloworld_pb2_grpc
def run():
# 連接 rpc 服務器
channel = grpc.insecure_channel('localhost:50051')
# 調用 rpc 服務
stub = helloworld_pb2_grpc.GreeterStub(channel)
response = stub.SayHello(helloworld_pb2.HelloRequest(name='czl'))
print("Greeter client received: " + response.message)
if __name__ == '__main__':
run()
三、K8S CRI接口
3.1 CRI是什么?
CRI(Container Runtime Interface)容器運行時接口,定義了容器和鏡像的服務的接口。因為容器運行時與鏡像的生命周期是彼此隔離的,因此需要定義兩個服務。該接口使用Protocol Buffer,基於gRPC。使用CRI接口,我們可以通過rpc的方式調用管理k8s集群容器。
3.2 iSula CRI
ISulad使用CRI接口,實現了和kubernetes 的對接。接下來演示python iSula CRI接口的調用。
首先需要通過grpc-io工具編譯CRI proto文件生成核心python模塊並開啟iSulad-CRI服務。
import grpc
import api_pb2_grpc
import api_pb2
if __name__ == '__main__':
# connect to rpc service
channel = grpc.insecure_channel('unix:///var/run/isulad.sock')
# rpc server: container and image server
runtime_stub = api_pb2_grpc.RuntimeServiceStub(channel)
image_stub = api_pb2_grpc.ImageServiceStub(channel)
# get iSulad version
response = runtime_stub.Version(api_pb2.VersionRequest())
print(response)
其余CRI API調用方式類似,具體參考其proto文件。
