Python3實現RPC

RPC,RPC(Remote Procedure Call)是指遠程過程調用,它是一種通過網絡從遠程計算機程序上請求服務,而不需要了解底層網絡技術的協議。RPC協議假定某些傳輸協議的存在,如TCP或UDP,爲通信程序之間攜帶信息數據。在OSI網絡通信模型中,RPC跨越了傳輸層和應用層。RPC使得開發包括網絡分佈式多程序在內的應用程序更加容易。

RPC採用客戶機/服務器模式。請求程序就是一個客戶機,而服務提供程序就是一個服務器。首先,客戶機調用進程發送一個有進程參數的調用信息到服務進程,然後等待應答信息。在服務器端,進程保持睡眠狀態直到調用信息到達爲止。當一個調用信息到達,服務器獲得進程參數,計算結果,發送答覆信息,然後等待下一個調用信息,最後,客戶端調用進程接收答覆信息,獲得進程結果,然後調用執行繼續進行。

它是一種遠程調用函數接口的方式,客戶端和服務端之間約定一種契約(函數接口),然後服務端一直等待客戶端的調用。有點像平常的WEB網絡請求,不過這種方式非常輕量,不涉及HTTP這些東西,待會可以看到實現很簡單。

上面說了,一種用途是在多臺服務器之間互相進行調用,另一個用途則在於,不同編程語言之間都支持這種方式,像Python更是內置對其的支持,不需要額外安裝什麼庫,所以可以直接在多語言的服務器之間互相進行調用,很簡單。

xmlrpc庫

在Python2中,服務端需要用到SimpleXMLRPCServer庫,客戶端需要用到ServerProxy庫,而在Python3中,兩者被整合到了同一個xmlrpc庫中,分爲xmlrpc.serverxmlrpc.client兩部分。所以如果在Python3下使用,就需要導入這個庫了。

1. 簡單的服務器端

服務器端像web請求一樣,需要確定供客戶端訪問的url和端口號,以及供客戶端調用的方法實現,最後要讓我們服務器一直處於等待被訪問的狀態,下面服務器簡單實現返回當前時間,源文件:server.py -

import datetime
from xmlrpc.server import SimpleXMLRPCServer
import xmlrpc.client

def today():
    today = datetime.datetime.today()
    return xmlrpc.client.DateTime(today)

server = SimpleXMLRPCServer(("localhost", 8000))
print("Listening on port 8000...")
server.register_function(today, "today")
server.serve_forever()

創建客戶端,調用服務器端過程得到當前時間。源文件:client.py -

import xmlrpc.client
import datetime

proxy = xmlrpc.client.ServerProxy("http://localhost:8000/")

today = proxy.today()
# convert the ISO8601 string to a datetime object
converted = datetime.datetime.strptime(today.value, "%Y%m%dT%H:%M:%S")
print("Today: %s" % converted.strftime("%d.%m.%Y, %H:%M"))

首先運行 server.py,然後再運行client.py,得到以下結果 -

Python3實現RPC

示例2

二進制對象的示例用法。在下面這個示例中,要通過XMLRPC傳輸圖片:

源文件:server.py -

from xmlrpc.server import SimpleXMLRPCServer
import xmlrpc.client

def python_logo():
    with open("python_logo.jpg", "rb") as handle:
        return xmlrpc.client.Binary(handle.read())

server = SimpleXMLRPCServer(("localhost", 8000))
print("Listening on port 8000...")
server.register_function(python_logo, 'python_logo')

server.serve_forever()

源文件:client.py -

import xmlrpc.client

proxy = xmlrpc.client.ServerProxy("http://localhost:8000/")
with open("/home/yiibai/yiibai-logo.jpg", "wb") as handle:
    handle.write(proxy.python_logo().data)

示例3

MultiCall對象提供了一種將對遠程服務器的多個調用封裝到單個請求中的方法。下面是該類的用法示例。

服務器源文件:server.py -

from xmlrpc.server import SimpleXMLRPCServer

def add(x, y):
    return x + y

def subtract(x, y):
    return x - y

def multiply(x, y):
    return x * y

def divide(x, y):
    return x // y

# A simple server with simple arithmetic functions
server = SimpleXMLRPCServer(("localhost", 8000))
print("Listening on port 8000...")
server.register_multicall_functions()
server.register_function(add, 'add')
server.register_function(subtract, 'subtract')
server.register_function(multiply, 'multiply')
server.register_function(divide, 'divide')
server.serve_forever()

客戶端源文件:client.py -

import xmlrpc.client

proxy = xmlrpc.client.ServerProxy("http://localhost:8000/")
multicall = xmlrpc.client.MultiCall(proxy)
multicall.add(7, 3)
multicall.subtract(7, 3)
multicall.multiply(7, 3)
multicall.divide(7, 3)
result = multicall()

print("7+3=%d, 7-3=%d, 7*3=%d, 7//3=%d" % tuple(result))