Python3的RPC实战

·  阅读 398

什么是RPC?

百度百科给出的解释是这样的:“RPC(Remote Procedure Call)——远程过程调用,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议”。

REST Vs RPC

微服务中服务之间的通信,大部分有两种:即为RESTful和RPC。 但是rest和rpc并不属于同一类对比的种类。rest是定义了一种设计风格和开发方式,rpc是定义了一种协议。

所属类别不同

REST,是Representational State Transfer 的简写,中文描述表述性状态传递(是指某个瞬间状态的资源数据的快照,包括资源数据的内容、表述格式(XML、JSON)等信息。)

REST 是一种软件架构风格。 这种风格的典型应用,就是HTTP。其因为简单、扩展性强的特点而广受开发者的青睐。

而RPC 呢,是 Remote Procedure Call Protocol 的简写,中文描述是远程过程调用,它可以实现客户端像调用本地服务(方法)一样调用服务器的服务(方法)。

RPC 是一种基于 TCP 的通信协议,按理说它和REST不是一个层面上的东西,不应该放在一起讨论,但是谁让REST这么流行呢,它是目前最流行的一套互联网应用程序的API设计标准,某种意义下,我们说 REST 可以其实就是指代 HTTP 协议。

使用方式不同

从使用上来看,HTTP 接口只关注服务提供方,对于客户端怎么调用并不关心。接口只要保证有客户端调用时,返回对应的数据就行了。而RPC则要求客户端接口保持和服务端的一致。

  • REST 是服务端把方法写好,客户端并不知道具体方法。客户端只想获取资源,所以发起HTTP请求,而服务端接收到请求后根据URI经过一系列的路由才定位到方法上面去
  • PRC是服务端提供好方法给客户端调用,客户端需要知道服务端的具体类,具体方法,然后像调用本地方法一样直接调用它。

面向对象不同

从设计上来看,RPC,所谓的远程过程调用 ,是面向方法的 ,REST:所谓的 Representational state transfer ,是面向资源的

序列化协议不同

接口调用通常包含两个部分,序列化和通信协议。

通信协议,上面已经提及了,REST 是 基于 HTTP 协议,而 RPC 可以基于 TCP/UDP,也可以基于 HTTP 协议进行传输的。

常见的序列化协议,有:json、xml、hession、protobuf、thrift、text、bytes等,REST 通常使用的是 JSON或者XML,而 RPC 使用的是 JSON-RPC,或者 XML-RPC。

应用

  • REST 接口更加规范,通用适配性要求高,建议对外的接口都统一成 REST(也有例外,比如我接触过 zabbix,其 API 就是基于 JSON-RPC 2.0协议的)。而组件内部的各个模块,可以选择 RPC,一个是不用耗费太多精力去开发和维护多套的HTTP接口,一个RPC的调用性能更
  • 从性能角度看,由于HTTP本身提供了丰富的状态功能与扩展功能,但也正由于HTTP提供的功能过多,导致在网络传输时,需要携带的信息更多,从性能角度上讲,较为低效。而RPC服务网络传输上仅传输与业务内容相关的数据,传输数据更小,性能更高。

python实现

Python3自带xmlrpc,第三方有提供jsonrpc和zerorpc。

xmlrpc

  • server(单线程)
from xmlrpc.server import SimpleXMLRPCServer
from xmlrpc.server import SimpleXMLRPCRequestHandler

class RequestHandler(SimpleXMLRPCRequestHandler):
    rpc_paths = ('/RPC2', '/RPC3')
    
class Calculate:
    def add(self, x, y):
        return x + y

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

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

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


with SimpleXMLRPCServer(('localhost', 8000),
                        requestHandler=RequestHandler) as server:
    server.register_introspection_functions()
    server.register_function(pow)


    @server.register_function(name='add1')
    def adder_function(x, y):
        return x + y + 1


    @server.register_function
    def mul(x, y):
        return x * y


    server.register_instance(Calculate())

    server.serve_forever()
复制代码
  • client
import xmlrpc.client

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

print(server.add(1, 2))
print(server.add1(1, 2))
print(server.pow(1, 2))
print(server.multiply(1, 2))
print(server.system.listMethods())

复制代码
  • server(多线程)
from socketserver import ThreadingMixIn


class ThreadXMLRPCServer(ThreadingMixIn, SimpleXMLRPCServer):
    pass  # 多线程


class MultRequestHandler(SimpleXMLRPCRequestHandler):
    rpc_paths = ('/MRPC',)


# 多线程
with ThreadXMLRPCServer(('localhost', 8001), requestHandler=MultRequestHandler, allow_none=True) as mserver:
    mserver.register_introspection_functions()

    mserver.register_multicall_functions()


    @mserver.register_function(name='add')
    def madd(a, b):
        return a + b


    mserver.serve_forever()
复制代码
  • 输出结果
## server端
127.0.0.1 - - [02/Feb/2021 10:21:14] "POST /RPC2 HTTP/1.1" 200 -
127.0.0.1 - - [02/Feb/2021 10:21:14] "POST /RPC2 HTTP/1.1" 200 -
127.0.0.1 - - [02/Feb/2021 10:21:14] "POST /RPC2 HTTP/1.1" 200 -
127.0.0.1 - - [02/Feb/2021 10:21:14] "POST /RPC2 HTTP/1.1" 200 -
127.0.0.1 - - [02/Feb/2021 10:21:14] "POST /RPC2 HTTP/1.1" 200 -
## client端
3
4
1
2
['add', 'add1', 'divide', 'mul', 'multiply', 'pow', 'subtract', 'system.listMethods', 'system.methodHelp', 'system.methodSignature']
复制代码

jsonrpc

  • 安装
# python3
pip3 install jsonrpclib-pelix
复制代码
  • server(单线程)
from jsonrpclib.SimpleJSONRPCServer import SimpleJSONRPCServer

# 单线程
server = SimpleJSONRPCServer(('localhost', 8080))
server.register_function(lambda x, y: x + y, 'add')
server.serve_forever()

复制代码
  • client
import jsonrpclib

server = jsonrpclib.Server("http://localhost:8080")
print(server.add(1, 2))
复制代码
  • server(多线程)
# 多线程
from jsonrpclib.threadpool import ThreadPool

# Setup the thread pool: between 0 and 10 threads
pool = ThreadPool(max_threads=10, min_threads=0)

# Don't forget to start it
pool.start()

# Setup the server
server = SimpleJSONRPCServer(('localhost', 8080))
server.set_notification_pool(pool)

# Register methods
server.register_function(pow)
server.register_function(lambda x, y: x + y, 'add')
server.register_function(lambda x: x, 'ping')

try:
    server.serve_forever()
finally:
    # Stop the thread pool (let threads finish their current task)
    pool.stop()
    server.set_notification_pool(None)
复制代码
  • 输出
# server
127.0.0.1 - - [02/Feb/2021 10:54:04] "POST / HTTP/1.1" 200 -
# client
3

Process finished with exit code 0
复制代码

zerorpc

xmlrpc和jsonrpc都是是用http+tcp的两种协议相结合的方式,zerorpc是完全给予TCP协议实现,相对http性能更高一些。

  • 安装
pip3 install zerorpc
复制代码
  • server
import zerorpc


class caculate(object):
    def hello(self, name):
        return 'hello, {}'.format(name)

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

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

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

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


s = zerorpc.Server(caculate())

s.bind("tcp://0.0.0.0:4242")
s.run()
复制代码
  • client
import zerorpc

c = zerorpc.Client()
c.connect("tcp://127.0.0.1:4242")
print(c.add(1, 2))
复制代码

性能测试

处于好奇,测试一下zerorpc和jsonrpc(单线程)情况下,统计client各循环10000次共使用的时间。

  • jsonrpc
All time is  27.96554207801819
复制代码
  • zerorpc
All time is  10.987850904464722
复制代码

zerorpc比jsonrpc性能还是很客观的。

参考

从0到1:全面理解RPC远程调用

分类:
后端
标签:
分类:
后端
标签: