前言

本文将会使用Python实现一个最简单的RPC框架,玩具向,不具有实用意义,但可以让你清醒的理解RPC框架的几个组成部分,只是比看Python自带的xmlrpc清晰。

本文需要一点Python socket基础。

如果你对Python Socket基础方面的内容不是很熟悉,推荐阅读Real Python的「Socket Programming in Python (Guide)」

吐槽一下VSCode,在开发一些比较复杂的Python项目时,VSCode的debug功能让人感到蛋疼,询问了Windows下使用VSCode的同事,都没有这样的问题,不清楚VSCode对Mac的支持是否存在问题,还只是我单纯的不会用:(

本文代码比较简单,所以还是使用VSCode进行开发。那我们开始吧!

回顾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端都写好了,跑一下吧。

总结一下

通过上述代码,再次理解一下RPC中这几个重要的概念,理解的是不是深入了一下。

客户端(Client):服务调用方。

客户端存根(Client Stub):存放服务端地址信息,将客户端的请求参数数据信息打包成网络消息,再通过网络传输发送给服务端。

服务端存根(Server Stub):接收客户端发送过来的请求消息并进行解包,然后再调用本地服务进行处理。

服务端(Server):服务的真正提供者。

Network Service:底层传输,可以是 TCP 或 HTTP。

开源的RPC框架肯定不是这么简单的,其中考虑了特别的边界条件以及各种优化,但RPC本身确是简单的。

结尾

最近一直在研究Docker,尝试通过Go来写一个玩具docker,后面弄出来,会分享一下Go与docker相关的内容。

下篇文章见,对了,有帮助的话,点「在看」或「赞赏」进行催更吧。

python实现rpc框架_使用Python实现RPC框架相关推荐

  1. python 报表框架_自学Python还不晚!阿里达摩院推荐的Python学习教程,分享给大家...

    在互联网信息爆炸的时代,我们经常面临各种与信息搜集有关的事情,但是信息来源网站多,信息量大,如果采用常规的人工搜索+office软件整理,往往要花费大量的时间. 这里为大家介绍一个门槛低.易上手的工具 ...

  2. python怎么更新数据库_在Python的Django框架中更新数据库数据的方法

    先使用一些关键参数创建对象实例,如下: >>> p = Publisher(name='Apress', ... address='2855 Telegraph Ave.', ... ...

  3. python nameko用于生产_用 Python、 RabbitMQ 和 Nameko 实现微服务

    "微服务是一股新浪潮" - 现如今,将项目拆分成多个独立的.可扩展的服务是保障代码演变的最好选择.在 Python 的世界里,有个叫做 "Nameko" 的框架 ...

  4. python新手练习项目_适合Python 新手的5大练手项目,你练了么?

    已经学习了一段时间的Python,如果你看过之前W3Cschool的文章,就知道是时候该进去[项目]阶段了. 但是在练手项目的选择上,还存在疑问?不知道要从哪种项目先下手? W3Cschool首先有两 ...

  5. python configure函数 循环_使用python统计git仓库中频繁修改的热点函数

    本篇博客以开源代码RT-Thread为例,描述了如何使用python扫描统计代码中频繁修改的函数,帮助我们发现系统中需求变化和BUG制造的重灾区. 需求背景 最近在学习设计模式时,印象深刻的一句话就是 ...

  6. python大数据免费_用python做大数据

    不学Python迟早会被淘汰?Python真有这么好的前景? 最近几年Python编程语言在国内引起不小的轰动,有超越Java之势,本来在美国这个编程语言就是最火的,应用的非常非常的广泛,而Pytho ...

  7. python代码设计测试用例_(appium+python)UI自动化_08_unittest编写测试用例

    前言 unittest是python自带的单元测试框架,类似于Junit(Java单元测试框架).支持自动化测试,可编写测试前置&后置条件,并且可批量运行测试用例并生成测试报告. 使用unit ...

  8. python大牛 关东升_《Python从小白到大牛》第4章 Python语法基础

    本章主要为大家介绍Python的一些语法,其中包括标识符.关键字.常量.变量.表达式.语句.注释.模块和包等内容. 标识符和关键字 任何一种计算机语言都离不开标识符和关键字,因此下面将详细介绍Pyth ...

  9. python要学哪些_学python都要学哪些内容?

    如果培训都是分五个阶段,第一阶段学习Python核心编程,主要是Python语言基础.Linux.MySQL,前期学习Python编程语言基础内容;中期主要涉及OOP基础知识,学习后应该能自己处理OO ...

最新文章

  1. IntelliJ IDEA 更新后,电脑卡成球,该如何优化?
  2. 陈键飞:基于随机量化的高效神经网络训练理论及算法
  3. php 折叠菜单,SlashdotMenu 折叠菜单
  4. [MySQL FAQ]系列 — 线上环境到底要不要开启query cache
  5. phpcms_v9推送到其他栏目后再在其他栏目删除导致数据库出错
  6. 阿里巴巴发布智能运维故障管理AI+生态计划
  7. pytorch torchvision类
  8. 面向对象闲话(一)——什么是对象
  9. Java运算符和类型转换
  10. python整数类型没有取值范围限制_python综述(一),复习
  11. 惠普计算机开机半花屏,惠普电脑花屏怎么办
  12. 七夕祝福网页制作_七夕福利 | 程序员土味情话大PK,专属好礼等你拿
  13. 关于mac m1 安装安卓模拟器
  14. linux shell搜索某个字符串,然后在后面加上字符串?字符串后面插入字符串?sed字符串后面插入字符串?...
  15. 从技术债务的角度, 谈谈重构
  16. 新势力新名片-上海度普新能源通过ASPICE CL2评估!
  17. 应用宝shangjia安全评估报告_《安全评估报告》提交指引
  18. User Agreement(APP用户协议)
  19. Qt编写地图综合应用55-海量点位标注
  20. 美团即时零售的优势不止“快”

热门文章

  1. 剑指Offer - 面试题65. 不用加减乘除做加法(位运算,要看哦)
  2. mysq命令行导出sql_mysql 命令行导入导出 sql
  3. 公安计算机技能测试题库,2018公安文职考试题库:行政职业能力测验
  4. j2ee和mysql怎么连接_J2EE数据库连接不再烦恼
  5. 数据库练习(二)三个数据库根据指定id获取name和存储数据库名称
  6. 计算机考研英语什么时候考试,计算机考研:早期复习三大注意事项
  7. 拦截游戏窗口被移动_Ruined King官网版-拳头rpg Ruined King游戏最新版下载v1.0
  8. 别再双塔了!谷歌提出DSI索引,检索效果吊打双塔,零样本超BM25!
  9. 再论推荐特征与embedding生成
  10. 吴恩达:机器学习应以数据为中心