предисловие
В этой статье для реализации простейшей среды RPC будет использоваться Python.Это непрактично, но может помочь вам ясно понять некоторые компоненты среды RPC, что только яснее, чем xmlrpc, поставляемый с Python.
Для этой статьи требуется немного основ сокетов Python.
Если вы не очень хорошо знакомы с основами Python Sockets, рекомендуется прочитать Real Python's "Socket Programming in Python (Guide)"
Tucao о VSCode, при разработке некоторых более сложных проектов Python функция отладки VSCode заставляет людей чувствовать себя больными.Я спросил своих коллег, которые используют VSCode под Windows, и такой проблемы нет.Я не знаю, есть ли проблема с Поддержка VSCode для Mac, просто я не могу им пользоваться :(
Код в этой статье относительно прост, поэтому я все еще использую VSCode для разработки. Итак, приступим!
Обзор RPC
- Клиент (Client): вызывающий абонент службы.
- Клиентская заглушка: сохраняет информацию об адресе сервера, упаковывает информацию о параметрах запроса клиента в сетевое сообщение, а затем отправляет ее на сервер через передачу по сети.
- Заглушка сервера: получите сообщение запроса, отправленное клиентом, и распакуйте его, а затем вызовите локальную службу для обработки.
- Сервер (Server): реальный поставщик услуги.
- Сетевая служба: базовый транспорт, который может быть TCP или HTTP.
реализовать jsonrpc
Прежде чем реализовать его, давайте кратко разберемся в общей идее.
1. Сетевая служба реализована напрямую с использованием API-интерфейсов, связанных с сокетами Python. 2. Используйте JSON для передачи данных, которые будут сжаты в двоичные файлы на уровне сокетов, нам не нужно заботиться
Имитируя xmlrpc, и клиент, и сервер реализованы с использованием механизма множественного наследования Minix, каждый класс отвечает за свои дела, и в конечном итоге выставлены только ограниченные методы в одном классе.
Начнем со стороны клиента.
# client.py
import rpcclient
c = rpcclient.RPCClient()
c.connect('127.0.0.1', 5000)
res = c.add(1, 2, c=3)
print(f'res: [{res}]')
Создайте экземпляр класса rpcclient.RPCClient, затем вызовите метод подключения, чтобы связать серверную сторону, а затем напрямую вызовите метод добавления на стороне сервера.Эффект этого метода заключается в накоплении входящих данных и возврате накопленного результата, и, наконец, печати результат, возвращаемый методом add.
Класс RPCClient наследуется от класса TCPClient и класса RPCStub.
# rpclient.py
class RPCClient(TCPClient, RPCStub):
pass
Среди них TCPClient отвечает за реализацию TCP-соединения через Socket и запрос данных, в то время как класс RPCStub в основном упаковывает соответствующую информацию на стороне клиента, вызывая метод на стороне сервера, а затем вызывает метод в классе TCPClient для его отправки. Два класса также реализованы в rpclient.py В файле код выглядит следующим образом.
class TCPClient(object):
def __init__(self):
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
def connect(self, host, port):
'''链接Server端'''
self.sock.connect((host, port))
def send(self, data):
'''将数据发送到Server端'''
self.sock.send(data)
def recv(self, length):
'''接受Server端回传的数据'''
return self.sock.recv(length)
class RPCStub(object):
def __getattr__(self, function):
def _func(*args, **kwargs):
d = {'method_name': function, 'method_args': args, 'method_kwargs': kwargs}
self.send(json.dumps(d).encode('utf-8')) # 发送数据
data = self.recv(1024) # 接收方法执行后返回的结果
return data
setattr(self, function, _func)
return _func
Класс TCPClient — это операция обычного Socket API, Само собой разумеется, мы в основном рассматриваем класс RPCStub.
Когда мы звоним на стороне клиентаres = c.add(1, 2, c=3), RPCStub в__getattr__метод, этот метод будет отправлять метод, параметры и другую информацию, вызываемую на стороне клиента, через метод отправки класса TCPServer, а отправляемые данные форматируются в формате JSON, что удобно для декодирования на стороне сервера, а затем вызывает метод recv для ожидания возврата соответствующих данных со стороны сервера.
Поскольку сам класс RPCClient не имеет метода добавления, чтобы позволить пользователю напрямую вызывать метод на стороне сервера со стороны клиента, сначала используйте метод__getattr__Построен_funcМетод и установите его в класс RPCClient с помощью метода setattr. В настоящее время класс имеет сопоставление, соответствующее методу на стороне сервера.
При вызове метода add соответствующий_funcметод отправки данных на сервер.
Клиентская часть делается так, а потом реализовать серверную, не нервничайте, это очень просто.
Серверная часть используется следующим образом.
# server.py
import rpcserver
def add(a, b, c=10):
sum = a + b + c
return sum
s = rpcserver.RPCServer()
s.register_function(add) # 注册方法
s.loop(5000) # 传入要监听的端口
Создайте экземпляр класса rpcserver.RPCServer, затем передайте метод, который будет вызываться клиентом, через метод register_function, а затем вызовите метод цикла, чтобы передать порт, который нужно отслеживать.Реализация класса RPCServer выглядит следующим образом.
# rpcserver.py
class RPCServer(TCPServer, JSONRPC, RPCStub):
def __init__(self):
TCPServer.__init__(self)
JSONRPC.__init__(self)
RPCStub.__init__(self)
def loop(self, port):
# 循环监听 5000 端口
self.bind_listen(port)
print('Server listen 5000 ...')
while True:
self.accept_receive_close()
def on_msg(self, data):
return self.call_method(data)
RPCServer наследуется от TCPServer, JSONRPC и RPCStub.Эти классы также реализованы в файле rpcserver.py и дают подробные комментарии, поэтому они подробно объясняются.
class TCPServer(object):
def __init__(self):
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
def bind_listen(self, port):
self.sock.bind(('0.0.0.0', port))
self.sock.listen(5)
def accept_receive_close(self):
'''获取Client端信息'''
(client_socket, address) = self.sock.accept()
msg = client_socket.recv(1024)
data = self.on_msg(msg)
client_socket.sendall(data) # 回传
client_socket.close()
class JSONRPC(object):
def __init__(self):
self.data = None
def from_data(self, data):
'''解析数据'''
self.data = json.loads(data.decode('utf-8'))
def call_method(self, data):
'''解析数据,调用对应的方法变将该方法执行结果返回'''
self.from_data(data)
method_name = self.data['method_name']
method_args = self.data['method_args']
method_kwargs = self.data['method_kwargs']
res = self.funs[method_name](*method_args, **method_kwargs)
data = {"res": res}
return json.dumps(data).encode('utf-8')
class RPCStub(object):
def __init__(self):
self.funs = {}
def register_function(self, function, name=None):
'''Server端方法注册,Client端只可调用被注册的方法'''
if name is None:
name = function.__name__
self.funs[name] = function
На данный момент и Клиент, и Сервер написаны, давайте запустим.
в заключении
С помощью приведенного выше кода давайте снова разберемся в этих важных концепциях RPC и поймем, является ли он углубленным.
- Клиент (Client): вызывающий абонент службы.
- Клиентская заглушка: сохраняет информацию об адресе сервера, упаковывает информацию о параметрах запроса клиента в сетевое сообщение, а затем отправляет ее на сервер через передачу по сети.
- Заглушка сервера: получите сообщение запроса, отправленное клиентом, и распакуйте его, а затем вызовите локальную службу для обработки.
- Сервер (Server): реальный поставщик услуги.
- Сетевая служба: базовый транспорт, который может быть TCP или HTTP.
Фреймворки RPC с открытым исходным кодом, конечно, не так просты, учитывая специальные граничные условия и различные оптимизации, но сам RPC прост.
конец
В последнее время я изучаю Docker, пытаюсь написать игрушечный докер через Go, а контентом, связанным с Go и докером, поделюсь позже.
Увидимся в следующей статье.Кстати, если она была полезной, пожалуйста, нажмите "Смотрел" или "Ценю", чтобы напомнить об этом.