python实现一个简单RPC框架的示例


Posted in Python onOctober 28, 2020

本文需要一点Python socket基础。

回顾RPC

python实现一个简单RPC框架的示例

  • 客户端(Client):服务调用方。
  • 客户端存根(Client Stub):存放服务端地址信息,将客户端的请求参数数据信息打包成网络消息,再通过网络传输发送给服务端。
  • 服务端存根(Server Stub):接收客户端发送过来的请求消息并进行解包,然后再调用本地服务进行处理。
  • 服务端(Server):服务的真正提供者。
  • Network Service:底层传输,可以是 TCP 或 HTTP。

实现jsonrpc

在实现前,简单理一下整体思路。

1、Network Service 直接使用Python Socket相关的API实现 2.传输数据使用JSON,在Socket层会被压成二进制,我们无需关心。

模仿xmlrpc,Client与Server都采用Minix多继承机制来实现,每个类负责自身的事情,最终暴露出现的只有一个类中有限的方法。

先从Client端开始实现。

# 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类,然后调用connect方法链接Server端,随后直接调用Server端的add方法,该方法的效果就是将传入的数据进行累加并将累加的结果返回,最后将add方法返回的结果打印出了。

RPCClient类继承于TCPClient类与RPCStub类。

# rpclient.py

class RPCClient(TCPClient, RPCStub):

    pass

其中TCPClient负责通过Socket实现TCP链接并将数据请求过去,而RPCStub类主要将Client端调用Server端方法的相关信息打包,然后调用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类。

当我们在Client端调用res = c.add(1, 2, c=3)时,会执行RPCStub中的__getattr__方法,该方法会将Client端调用的方法、参数等信息通过TCPServer类的send方法发送,发送数据进行了JSON格式化,方便Server端解码,随后便调用recv方法等待Server端相应的数据返回。

因为RPCClient类本身没有add方法,为了让用户做到Client端直接调用Server端方法的形式,先利用__getattr__构建了_func方法,并将其通过setattr方法设置到RPCClient类中,此时该类就有Server端方法对应的映射了。

调用add方法,就调用了对应的_func方法,将数据发送至Server端。

Client端就这样搞定了,接着来实现Server端,不用紧张,非常简单。

Server端的使用方式如下。

# 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方法将想被Client端调用的方法传入,随后调用loop方法,将要监听的端口传入,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

至此,Client端和Server端都写好了。

测试:

python实现一个简单RPC框架的示例

以上就是python实现一个简单RPC框架的示例的详细内容,更多关于python 实现RPC框架的资料请关注三水点靠木其它相关文章!

Python 相关文章推荐
Python3写入文件常用方法实例分析
May 22 Python
离线安装Pyecharts的步骤以及依赖包流程
Apr 23 Python
python模块之paramiko实例代码
Jan 31 Python
对python中执行DOS命令的3种方法总结
May 12 Python
搭建python django虚拟环境完整步骤详解
Jul 08 Python
Django ImageFiled上传照片并显示的方法
Jul 28 Python
pytorch 更改预训练模型网络结构的方法
Aug 19 Python
关于Numpy数据类型对象(dtype)使用详解
Nov 27 Python
pytorch方法测试详解——归一化(BatchNorm2d)
Jan 15 Python
python字典key不能是可以是啥类型
Aug 04 Python
python如何发送带有附件、正文为HTML的邮件
Feb 27 Python
python如何利用traceback获取详细的异常信息
Jun 05 Python
pycharm永久激活超详细教程
Oct 29 #Python
linux mint中搜狗输入法导致pycharm卡死的问题
Oct 28 #Python
浅谈Python __init__.py的作用
Oct 28 #Python
详解Selenium-webdriver绕开反爬虫机制的4种方法
Oct 28 #Python
详解Python流程控制语句
Oct 28 #Python
详解selenium + chromedriver 被反爬的解决方法
Oct 28 #Python
手把手教你从PyCharm安装到激活(最新激活码),亲测有效可激活至2089年
Nov 25 #Python
You might like
php连接Access数据库错误及解决方法
2013/06/20 PHP
PHP模板引擎smarty详细介绍
2015/05/26 PHP
php封装好的人民币数值转中文大写类
2015/12/20 PHP
PHP实现接收二进制流转换成图片的方法
2017/01/10 PHP
form自动提交实例讲解
2017/07/10 PHP
javascript 字符 Escape,encodeURI,encodeURIComponent
2009/07/09 Javascript
jQuery Tools tab(幻灯片)
2012/07/14 Javascript
解析Jquery的LigerUI如何实现文件上传
2013/07/09 Javascript
多个jQuery版本共存的处理方案
2015/03/17 Javascript
JQuery入门基础小实例(1)
2015/09/17 Javascript
浅谈js函数中的实例对象、类对象、局部变量(局部函数)
2016/11/20 Javascript
详解使用Vue.Js结合Jquery Ajax加载数据的两种方式
2017/01/10 Javascript
AngularJS Select(选择框)使用详解
2017/01/18 Javascript
浅谈vue实现数据监听的函数 Object.defineProperty
2017/06/08 Javascript
使用Vue.set()方法实现响应式修改数组数据步骤
2019/11/09 Javascript
[01:25]DOTA2超级联赛专访iG 将调整状态找回自己
2013/06/05 DOTA
[05:03]显微镜下的DOTA2第十期——Ti3豪之超神幽鬼
2014/06/23 DOTA
[57:55]EG vs Fnatic 2018国际邀请赛小组赛BO2 第一场 8.19
2018/08/21 DOTA
利用Python中SocketServer 实现客户端与服务器间非阻塞通信
2016/12/15 Python
python学生管理系统代码实现
2020/04/05 Python
关于python多重赋值的小问题
2019/04/17 Python
python从内存地址上加载python对象过程详解
2020/01/08 Python
Windows下Pycharm远程连接虚拟机中Centos下的Python环境(图文教程详解)
2020/03/19 Python
关于keras.layers.Conv1D的kernel_size参数使用介绍
2020/05/22 Python
利用Python批量识别电子账单数据的方法
2021/02/08 Python
HTML5中微数据概述及在搜索引擎中的使用举例
2013/02/07 HTML / CSS
英国厨房与餐具用品为主的设计品牌:Joseph Joseph
2018/04/26 全球购物
留学自荐信
2013/10/10 职场文书
青年教师典范事迹材料
2014/01/31 职场文书
红头文件任命书范本
2014/06/05 职场文书
八项规定对照检查材料
2014/08/31 职场文书
2015年幼儿园中班下学期工作总结
2015/05/22 职场文书
女性健康讲座主持词
2015/07/04 职场文书
Python数据分析入门之教你怎么搭建环境
2021/05/13 Python
MySQL命令无法输入中文问题的解决方式
2021/08/30 MySQL
详解Vue的列表渲染
2021/11/20 Vue.js