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,得到以下結果 -

範例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))