如何仅使用Python内置模块构建REST与RPC服务的实战案例?

2026-04-20 04:201阅读0评论SEO教程
  • 内容介绍
  • 文章标签
  • 相关推荐

本文共计2244个文字,预计阅读时间需要9分钟。

如何仅使用Python内置模块构建REST与RPC服务的实战案例?

目录+ 写在前端+ 创建TCP服务+ 创建UDP服务+ 创建一个简单的REST接口+ WSGI标准简单分析+ 作为客户端与HTTP服务交互+ 通过XML-RPC实现简单的远程调用+ 通过multiprocessing实现RPC调用+ 写在前端和和小编

目录
  • 写在前面
  • 创建TCP服务
  • 创建UDP服务
  • 创建一个简单的REST接口
  • WSGI标准简单分析
  • 作为客户端与HTTP服务交互
  • 通过XML-RPC实现简单的远程调用
  • 通过 multiprocessing 实现RPC调用

写在前面

和小伙伴们分享一些Python 网络编程的一些笔记,博文为《Python Cookbook》读书后笔记整理

博文涉及内容包括:

  • TCP/UDP服务构建
  • 不使用框架创建一个REST风格的web服务
  • 基于XML-RPC实现简单的RPC
  • 基于multiprocessing.connection实现简单的RPC
  • python实现作为客户端与HTTP服务交互

在Python中,构建一个静态Web服务器,只需要 python3 -m localhost:15001', allow_none=True) >>> s.set('foo','bar') >>> s.set('spam', [1, 2, 3]) >>> s.keys() ['foo', 'spam'] >>> s.get('foo') 'bar' >>> s.get('spam') [1, 2, 3] >>> s.delete('spam') >>> s.exists('spam') False >>>

XML-RPC 可以让很容易的构造一个简单的远程调用服务。所需要做的仅仅是创建一个服务器实例,通过它的方法register_function()来注册函数,然后使用方法serve_forever()启动它。在上面将这些步骤放在一起写到一个类中

这并不是必须的。还可以像下面这样创建一个服务器:

from xmlrpc.server import SimpleXMLRPCServer from xmlrpc.server import SimpleXMLRPCServer def add(x,y): return x+y serv = SimpleXMLRPCServer(('', 15000)) serv.register_function(add) serv.serve_forever()

XML-RPC 暴露出来的函数只能适用于部分数据类型,比如字符串、整形、列表和字典,不应该将 XML-RPC 服务以公共 API 的方式暴露出来。

XML-RPC 的一个缺点是它的性能。SimpleXMLRPCServer 的实现是单线程的,所以它不适合于大型程序

由于 XML-RPC 将所有数据都序列化为 XML 格式,所以它会比其他的方式运行的慢一些。但是它也有优点,这种方式的编码可以被绝大部分其他编程语言支持。通过使用这种方式,其他语言的客户端程序都能访问的服务。

通过 multiprocessing 实现RPC调用

在一个消息传输层如 sockets、multiprocessing.connections或zeroMQ的基础之上实现一个简单的远程过程调用(RPC)

函数请求参数返回值使用pickle编码后,在不同的解释器直接传送pickle字节字符串,可以很容易的实现RPC。下面是一个简单的PRC处理器,可以被整合到一个服务器中去:

RPC 服务端

""" @File : rpcserver.py @Author : Li Ruilong @Version : 1.0 @Desc : 远程调用服务 """ # here put the import lib import pickle from multiprocessing.connection import Listener from threading import Thread """ @Time : 2022/07/08 20:28:02 @Author : Li Ruilong @Version : 1.0 @Desc : None Args: 远程调用处理器 Returns: void """ class RPCHandler: def __init__(self): self._functions = {} """ @Time : 2022/07/08 20:16:47 @Author : Li Ruilong @Version : 1.0 @Desc : 函数注册 Args: func Returns: void """ def register_function(self, func): self._functions[func.__name__] = func """ @Time : 2022/07/08 20:17:51 @Author : Li Ruilong @Version : 1.0 @Desc : 调用函数 Args: connection Returns: void """ def handle_connection(self, connection): try: while True: func_name, args, kwargs = pickle.loads(connection.recv()) try: print("调用函数:",(func_name, args, kwargs)) r = self._functions[func_name](*args,**kwargs) print("返回结果:",r) connection.send(pickle.dumps(r)) except Exception as e: connection.send(pickle.dumps(e)) except Exception as e: pass def rpc_server(handler, address, authkey): sock = Listener(address, authkey=authkey) while True: client = sock.accept() t = Thread(target=handler.handle_connection, args=(client,)) t.daemon = True print("函数开始执行") t.start() def add(x, y): return x + y def sub(x, y): return x - y if __name__ == '__main__': print(format("开始加载RPC处理器",'》<20')) handler = RPCHandler() print(format("处理器加载完成,注册函数",'》<20')) handler.register_function(add) handler.register_function(sub) print(format("函数注册成功,服务启动",'》<20')) rpc_server(handler, ('localhost', 17000), authkey=b'peekaboo')

RPC 客户端

import pickle from multiprocessing.connection import Client class RPCProxy: def __init__(self, connection): self._connection = connection def __getattr__(self, name): print("开始调用函数",name) def do_rpc(*args, **kwargs): self._connection.send(pickle.dumps((name, args, kwargs))) result = pickle.loads(self._connection.recv()) print("返回结果",result) if isinstance(result, Exception): raise result return result return do_rpc c = Client(('localhost', 17000), authkey=b'peekaboo') print(format("建立连接,创建RPC代理",'》<30'),c) proxy = RPCProxy(c) print(format("创建代理成功",'》<30')) print("add(2, 3) = ",proxy.add(2, 3) ) print("sub(2, 3) = ", proxy.sub(2, 3))

D:\python\Python310\python.exe D:/python/code/rabbit_mq_demo/rpcserver.py
开始加载RPC处理器》》》》》》》》》》
处理器加载完成,注册函数》》》》》》》》
函数注册成功,服务启动》》》》》》》》》
函数开始执行
调用函数: ('add', (2, 3), {})
返回结果: 5
调用函数: ('sub', (2, 3), {})
返回结果: -1
==============
D:\python\Python310\python.exe D:/python/code/rabbit_mq_demo/RPC.py
建立连接,创建RPC代理》》》》》》》》》》》》》》》》》》 <multiprocessing.connection.Connection object at 0x00DFACA0>
创建代理成功》》》》》》》》》》》》》》》》》》》》》》》》
开始调用函数 add
返回结果 5
add(2, 3) = 5
开始调用函数 sub
返回结果 -1
sub(2, 3) = -1

Process finished with exit code 0

RPCHandler和RPCProxy的基本思路是很比较简单的。

如果一个客户端想要调用一个远程函数,比如foo(1,2,z=3),代理类创建一个包含了函数名和参数的元组(foo’,(1,2),{‘z’:3})。这个元组被 pickle 序列化后通过网络连接发生出去。

由于底层需要依赖 pickle,那么安全问题就需要考虑了(因为一个聪明的黑客可以创建特定的消息,能够让任意函数通过 pickle反序列化后被执行)。

因此永远不要允许来自不信任或未认证的客户端的RPC。特别是绝对不要允许来自Internet的任意机器的访问,这种只能在内部被使用,位于防火墙后面并且不要对外暴露。

作为pickle的替代,也许可以考虑使用JSON、XML或一些其他的编码格式来序列化消息。

例如,本机实例可以很容易的改写成JSON编码方案。还需要将pickle.1oads()和pickle.dumps()替换成json.1oads()和json.dumps()即可:

# here put the import lib import json ........ def handle_connection(self, connection): try: while True: # 反序列化 func_name, args, kwargs = json.loads(connection.recv()) try: print("调用函数:",(func_name, args, kwargs)) r = self._functions[func_name](*args,**kwargs) print("返回结果:",r) # 序列化发送 connection.send(json.dumps(r)) except Exception as e: connection.send(json.dumps(e)) except Exception as e: pass ......

import json from multiprocessing.connection import Client class RPCProxy: def __init__(self, connection): self._connection = connection def __getattr__(self, name): print("开始调用函数",name) def do_rpc(*args, **kwargs): print("JSON 序列化后的值",json.dumps((name, args, kwargs))) self._connection.send(json.dumps((name, args, kwargs))) result = json.loads(self._connection.recv()) print("返回结果",result) if isinstance(result, Exception): raise result return result return do_rpc c = Client(('localhost', 17000), authkey=b'peekaboo') print(format("建立连接,创建RPC代理",'》<30'),c) proxy = RPCProxy(c) print(format("创建代理成功",'》<30')) print("add(2, 3) = ",proxy.add(2, 3) ) print("sub(2, 3) = ", proxy.sub(2, 3))

可以看到序列化后的结果:

D:\python\Python310\python.exe D:/python/code/rabbit_mq_demo/RPC.py
建立连接,创建RPC代理》》》》》》》》》》》》》》》》》》 <multiprocessing.connection.Connection object at 0x0078AD30>
创建代理成功》》》》》》》》》》》》》》》》》》》》》》》》
开始调用函数 add
JSON 序列化后的值 ["add", [2, 3], {}]
返回结果 5
add(2, 3) = 5
开始调用函数 sub
JSON 序列化后的值 ["sub", [2, 3], {}]
返回结果 -1
sub(2, 3) = -1

实现RPC的一个比较复杂的问题是如何去处理异常。至少,当方法产生异常时服务器不应该奔溃。因此,返回给客户端的异常所代表的含义就要好好设计了。

如果使用pickle,异常对象实例在客户端能被反序列化并抛出。如果使用其他的协议,那得想想另外的方法了。不过至少,应该在响应中返回异常字符串。在JSON的例子中就是使用的这种方式。

到此这篇关于Python用内置模块来构建REST服务与RPC服务实战的文章就介绍到这了,更多相关Python 构建REST服务 内容请搜索自由互联以前的文章或继续浏览下面的相关文章希望大家以后多多支持自由互联!

如何仅使用Python内置模块构建REST与RPC服务的实战案例?

本文共计2244个文字,预计阅读时间需要9分钟。

如何仅使用Python内置模块构建REST与RPC服务的实战案例?

目录+ 写在前端+ 创建TCP服务+ 创建UDP服务+ 创建一个简单的REST接口+ WSGI标准简单分析+ 作为客户端与HTTP服务交互+ 通过XML-RPC实现简单的远程调用+ 通过multiprocessing实现RPC调用+ 写在前端和和小编

目录
  • 写在前面
  • 创建TCP服务
  • 创建UDP服务
  • 创建一个简单的REST接口
  • WSGI标准简单分析
  • 作为客户端与HTTP服务交互
  • 通过XML-RPC实现简单的远程调用
  • 通过 multiprocessing 实现RPC调用

写在前面

和小伙伴们分享一些Python 网络编程的一些笔记,博文为《Python Cookbook》读书后笔记整理

博文涉及内容包括:

  • TCP/UDP服务构建
  • 不使用框架创建一个REST风格的web服务
  • 基于XML-RPC实现简单的RPC
  • 基于multiprocessing.connection实现简单的RPC
  • python实现作为客户端与HTTP服务交互

在Python中,构建一个静态Web服务器,只需要 python3 -m localhost:15001', allow_none=True) >>> s.set('foo','bar') >>> s.set('spam', [1, 2, 3]) >>> s.keys() ['foo', 'spam'] >>> s.get('foo') 'bar' >>> s.get('spam') [1, 2, 3] >>> s.delete('spam') >>> s.exists('spam') False >>>

XML-RPC 可以让很容易的构造一个简单的远程调用服务。所需要做的仅仅是创建一个服务器实例,通过它的方法register_function()来注册函数,然后使用方法serve_forever()启动它。在上面将这些步骤放在一起写到一个类中

这并不是必须的。还可以像下面这样创建一个服务器:

from xmlrpc.server import SimpleXMLRPCServer from xmlrpc.server import SimpleXMLRPCServer def add(x,y): return x+y serv = SimpleXMLRPCServer(('', 15000)) serv.register_function(add) serv.serve_forever()

XML-RPC 暴露出来的函数只能适用于部分数据类型,比如字符串、整形、列表和字典,不应该将 XML-RPC 服务以公共 API 的方式暴露出来。

XML-RPC 的一个缺点是它的性能。SimpleXMLRPCServer 的实现是单线程的,所以它不适合于大型程序

由于 XML-RPC 将所有数据都序列化为 XML 格式,所以它会比其他的方式运行的慢一些。但是它也有优点,这种方式的编码可以被绝大部分其他编程语言支持。通过使用这种方式,其他语言的客户端程序都能访问的服务。

通过 multiprocessing 实现RPC调用

在一个消息传输层如 sockets、multiprocessing.connections或zeroMQ的基础之上实现一个简单的远程过程调用(RPC)

函数请求参数返回值使用pickle编码后,在不同的解释器直接传送pickle字节字符串,可以很容易的实现RPC。下面是一个简单的PRC处理器,可以被整合到一个服务器中去:

RPC 服务端

""" @File : rpcserver.py @Author : Li Ruilong @Version : 1.0 @Desc : 远程调用服务 """ # here put the import lib import pickle from multiprocessing.connection import Listener from threading import Thread """ @Time : 2022/07/08 20:28:02 @Author : Li Ruilong @Version : 1.0 @Desc : None Args: 远程调用处理器 Returns: void """ class RPCHandler: def __init__(self): self._functions = {} """ @Time : 2022/07/08 20:16:47 @Author : Li Ruilong @Version : 1.0 @Desc : 函数注册 Args: func Returns: void """ def register_function(self, func): self._functions[func.__name__] = func """ @Time : 2022/07/08 20:17:51 @Author : Li Ruilong @Version : 1.0 @Desc : 调用函数 Args: connection Returns: void """ def handle_connection(self, connection): try: while True: func_name, args, kwargs = pickle.loads(connection.recv()) try: print("调用函数:",(func_name, args, kwargs)) r = self._functions[func_name](*args,**kwargs) print("返回结果:",r) connection.send(pickle.dumps(r)) except Exception as e: connection.send(pickle.dumps(e)) except Exception as e: pass def rpc_server(handler, address, authkey): sock = Listener(address, authkey=authkey) while True: client = sock.accept() t = Thread(target=handler.handle_connection, args=(client,)) t.daemon = True print("函数开始执行") t.start() def add(x, y): return x + y def sub(x, y): return x - y if __name__ == '__main__': print(format("开始加载RPC处理器",'》<20')) handler = RPCHandler() print(format("处理器加载完成,注册函数",'》<20')) handler.register_function(add) handler.register_function(sub) print(format("函数注册成功,服务启动",'》<20')) rpc_server(handler, ('localhost', 17000), authkey=b'peekaboo')

RPC 客户端

import pickle from multiprocessing.connection import Client class RPCProxy: def __init__(self, connection): self._connection = connection def __getattr__(self, name): print("开始调用函数",name) def do_rpc(*args, **kwargs): self._connection.send(pickle.dumps((name, args, kwargs))) result = pickle.loads(self._connection.recv()) print("返回结果",result) if isinstance(result, Exception): raise result return result return do_rpc c = Client(('localhost', 17000), authkey=b'peekaboo') print(format("建立连接,创建RPC代理",'》<30'),c) proxy = RPCProxy(c) print(format("创建代理成功",'》<30')) print("add(2, 3) = ",proxy.add(2, 3) ) print("sub(2, 3) = ", proxy.sub(2, 3))

D:\python\Python310\python.exe D:/python/code/rabbit_mq_demo/rpcserver.py
开始加载RPC处理器》》》》》》》》》》
处理器加载完成,注册函数》》》》》》》》
函数注册成功,服务启动》》》》》》》》》
函数开始执行
调用函数: ('add', (2, 3), {})
返回结果: 5
调用函数: ('sub', (2, 3), {})
返回结果: -1
==============
D:\python\Python310\python.exe D:/python/code/rabbit_mq_demo/RPC.py
建立连接,创建RPC代理》》》》》》》》》》》》》》》》》》 <multiprocessing.connection.Connection object at 0x00DFACA0>
创建代理成功》》》》》》》》》》》》》》》》》》》》》》》》
开始调用函数 add
返回结果 5
add(2, 3) = 5
开始调用函数 sub
返回结果 -1
sub(2, 3) = -1

Process finished with exit code 0

RPCHandler和RPCProxy的基本思路是很比较简单的。

如果一个客户端想要调用一个远程函数,比如foo(1,2,z=3),代理类创建一个包含了函数名和参数的元组(foo’,(1,2),{‘z’:3})。这个元组被 pickle 序列化后通过网络连接发生出去。

由于底层需要依赖 pickle,那么安全问题就需要考虑了(因为一个聪明的黑客可以创建特定的消息,能够让任意函数通过 pickle反序列化后被执行)。

因此永远不要允许来自不信任或未认证的客户端的RPC。特别是绝对不要允许来自Internet的任意机器的访问,这种只能在内部被使用,位于防火墙后面并且不要对外暴露。

作为pickle的替代,也许可以考虑使用JSON、XML或一些其他的编码格式来序列化消息。

例如,本机实例可以很容易的改写成JSON编码方案。还需要将pickle.1oads()和pickle.dumps()替换成json.1oads()和json.dumps()即可:

# here put the import lib import json ........ def handle_connection(self, connection): try: while True: # 反序列化 func_name, args, kwargs = json.loads(connection.recv()) try: print("调用函数:",(func_name, args, kwargs)) r = self._functions[func_name](*args,**kwargs) print("返回结果:",r) # 序列化发送 connection.send(json.dumps(r)) except Exception as e: connection.send(json.dumps(e)) except Exception as e: pass ......

import json from multiprocessing.connection import Client class RPCProxy: def __init__(self, connection): self._connection = connection def __getattr__(self, name): print("开始调用函数",name) def do_rpc(*args, **kwargs): print("JSON 序列化后的值",json.dumps((name, args, kwargs))) self._connection.send(json.dumps((name, args, kwargs))) result = json.loads(self._connection.recv()) print("返回结果",result) if isinstance(result, Exception): raise result return result return do_rpc c = Client(('localhost', 17000), authkey=b'peekaboo') print(format("建立连接,创建RPC代理",'》<30'),c) proxy = RPCProxy(c) print(format("创建代理成功",'》<30')) print("add(2, 3) = ",proxy.add(2, 3) ) print("sub(2, 3) = ", proxy.sub(2, 3))

可以看到序列化后的结果:

D:\python\Python310\python.exe D:/python/code/rabbit_mq_demo/RPC.py
建立连接,创建RPC代理》》》》》》》》》》》》》》》》》》 <multiprocessing.connection.Connection object at 0x0078AD30>
创建代理成功》》》》》》》》》》》》》》》》》》》》》》》》
开始调用函数 add
JSON 序列化后的值 ["add", [2, 3], {}]
返回结果 5
add(2, 3) = 5
开始调用函数 sub
JSON 序列化后的值 ["sub", [2, 3], {}]
返回结果 -1
sub(2, 3) = -1

实现RPC的一个比较复杂的问题是如何去处理异常。至少,当方法产生异常时服务器不应该奔溃。因此,返回给客户端的异常所代表的含义就要好好设计了。

如果使用pickle,异常对象实例在客户端能被反序列化并抛出。如果使用其他的协议,那得想想另外的方法了。不过至少,应该在响应中返回异常字符串。在JSON的例子中就是使用的这种方式。

到此这篇关于Python用内置模块来构建REST服务与RPC服务实战的文章就介绍到这了,更多相关Python 构建REST服务 内容请搜索自由互联以前的文章或继续浏览下面的相关文章希望大家以后多多支持自由互联!

如何仅使用Python内置模块构建REST与RPC服务的实战案例?