通过实例解析Python RPC实现原理及方法


Posted in Python onJuly 07, 2020

单线程同步

  • 使用socket传输数据
  • 使用json序列化消息体
  • struct将消息编码为二进制字节串,进行网络传输

消息协议

// 输入
{
  in: "ping",
  params: "ireader 0"
}

// 输出
{
  out: "pong",
  result: "ireader 0"
}

客户端 client.py

# coding: utf-8
# client.py

import json
import time
import struct
import socket


def rpc(sock, in_, params):
  response = json.dumps({"in": in_, "params": params}) # 请求消息体
  length_prefix = struct.pack("I", len(response)) # 请求长度前缀
  sock.sendall(length_prefix)
  sock.sendall(response)
  length_prefix = sock.recv(4) # 响应长度前缀
  length, = struct.unpack("I", length_prefix)
  body = sock.recv(length) # 响应消息体
  response = json.loads(body)
  return response["out"], response["result"] # 返回响应类型和结果

if __name__ == '__main__':
  s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  s.connect(("localhost", 8080))
  for i in range(10): # 连续发送10个rpc请求
    out, result = rpc(s, "ping", "ireader %d" % i)
    print out, result
    time.sleep(1) # 休眠1s,便于观察
  s.close() # 关闭连接

通过实例解析Python RPC实现原理及方法

服务端 blocking_single.py

# coding: utf8
# blocking_single.py

import json
import struct
import socket


def handle_conn(conn, addr, handlers):
  print addr, "comes"
  while True: # 循环读写
    length_prefix = conn.recv(4) # 请求长度前缀
    if not length_prefix: # 连接关闭了
      print addr, "bye"
      conn.close()
      break # 退出循环,处理下一个连接
    length, = struct.unpack("I", length_prefix)
    body = conn.recv(length) # 请求消息体
    request = json.loads(body)
    in_ = request['in']
    params = request['params']
    print in_, params
    handler = handlers[in_] # 查找请求处理器
    handler(conn, params) # 处理请求


def loop(sock, handlers):
  while True:
    conn, addr = sock.accept() # 接收连接
    handle_conn(conn, addr, handlers) # 处理连接


def ping(conn, params):
  send_result(conn, "pong", params)


def send_result(conn, out, result):
  response = json.dumps({"out": out, "result": result}) # 响应消息体
  length_prefix = struct.pack("I", len(response)) # 响应长度前缀
  conn.sendall(length_prefix)
  conn.sendall(response)


if __name__ == '__main__':
  sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 创建一个TCP套接字
  sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # 打开reuse addr选项
  sock.bind(("localhost", 8080)) # 绑定端口
  sock.listen(1) # 监听客户端连接
  handlers = { # 注册请求处理器
    "ping": ping
  }
  loop(sock, handlers) # 进入服务循环

通过实例解析Python RPC实现原理及方法

多线程同步

  • 使用线程库thread创建原生线程
  • 服务器可并行处理多个客户端

服务端 multithread.py

通过实例解析Python RPC实现原理及方法

多进程同步

  • Python的GIL导致单个进程只能占满一个CPU核心,多线程无法利用多核优势
  • os.fork()会生成子进程
  • 子进程退出后,父进程需使用waitpid系统调用收割子进程,防止其称为僵尸资源
  • 在子进程中关闭服务器套接字后,在父进程中也要关闭服务器套接字
  • 因为进程fork后,父子进程都有自己的套接字引用指向内核的同一份套接字对象,套接字引用计数为2,对套接字进程close,即将套接字对象的引用计数减1

PreForking同步

  • 进程比线程耗费资源,通过PreForking进程池模型对服务器开辟的进程数量进行限制,避免服务器负载过重
  • 如果并行的连接数量超过了prefork进程数量,后来的客户端请求将会阻塞

单进程异步

  • 通过事件轮询API,查询相关套接字是否有响应的读写事件,有则携带事件列表返回,没有则阻塞
  • 拿到读写事件后,可对事件相关的套接字进行读写操作
  • 设置读写缓冲区
  • Nginx/Nodejs/Redis都是基于异步模型
  • 异步模型编码成本高,易出错,通常在公司业务代码中采用同步模型,仅在讲究高并发高性能的场合才使用异步模型

PreForking异步

Tornado/Nginx采用了多进程PreForking异步模型,具有良好的高并发处理能力

通过实例解析Python RPC实现原理及方法

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
打印出python 当前全局变量和入口参数的所有属性
Jul 01 Python
python检测是文件还是目录的方法
Jul 03 Python
解决pycharm无法调用pip安装的包问题
May 18 Python
浅谈python3发送post请求参数为空的情况
Dec 28 Python
阿里云ECS服务器部署django的方法
Aug 29 Python
NumPy中的维度Axis详解
Nov 26 Python
Python迭代器模块itertools使用原理解析
Dec 11 Python
tensorflow模型继续训练 fineturn实例
Jan 21 Python
解决python-docx打包之后找不到default.docx的问题
Feb 13 Python
python 回溯法模板详解
Feb 26 Python
Python捕获异常堆栈信息的几种方法(小结)
May 18 Python
浅谈怎么给Python添加类型标注
Jun 08 Python
Keras预训练的ImageNet模型实现分类操作
Jul 07 #Python
Scrapy模拟登录赶集网的实现代码
Jul 07 #Python
scrapy框架携带cookie访问淘宝购物车功能的实现代码
Jul 07 #Python
Keras构建神经网络踩坑(解决model.predict预测值全为0.0的问题)
Jul 07 #Python
浅谈django框架集成swagger以及自定义参数问题
Jul 07 #Python
Django REST Swagger实现指定api参数
Jul 07 #Python
python中查看.db文件中表格的名字及表格中的字段操作
Jul 07 #Python
You might like
php.ini中的php-5.2.0配置指令详解
2008/03/27 PHP
php判断输入不超过mysql的varchar字段的长度范围
2011/06/24 PHP
zend framework重定向方法小结
2016/05/28 PHP
PHP实现路由映射到指定控制器
2016/08/13 PHP
PHP memcache在微信公众平台的应用方法示例
2017/09/13 PHP
ExtJS 学习专题(一) 如何应用ExtJS(附实例)
2010/03/11 Javascript
jquery $(this).attr $(this).val方法使用介绍
2013/10/08 Javascript
浅析javascript的间隔调用和延时调用
2014/11/12 Javascript
javascript中indexOf技术详解
2015/05/07 Javascript
实现高性能JavaScript之执行与加载
2016/01/30 Javascript
新入门node.js必须要知道的概念(必看篇)
2016/08/10 Javascript
vuejs父子组件之间数据交互详解
2017/08/09 Javascript
vue 使用Jade模板写html,stylus写css的方法
2018/02/23 Javascript
Webpack path与publicPath的区别详解
2018/05/03 Javascript
Angular 数据请求的实现方法
2018/05/07 Javascript
react 应用多入口配置及实践总结
2018/10/17 Javascript
在微信小程序中渲染HTML内容3种解决方案及分析与问题解决
2020/01/12 Javascript
Python使用函数默认值实现函数静态变量的方法
2014/08/18 Python
Python的字典和列表的使用中一些需要注意的地方
2015/04/24 Python
python下setuptools的安装详解及No module named setuptools的解决方法
2017/07/06 Python
Python爬虫使用Selenium+PhantomJS抓取Ajax和动态HTML内容
2018/02/23 Python
[原创]windows下Anaconda的安装与配置正解(Anaconda入门教程)
2018/04/05 Python
解决Shell执行python文件,传参空格引起的问题
2018/10/30 Python
利用python-docx模块写批量生日邀请函
2019/08/26 Python
使用Python画出小人发射爱心的代码
2019/11/23 Python
使用Nibabel库对nii格式图像的读写操作
2020/07/01 Python
PUMA官方商城:世界领先的运动品牌之一
2016/11/16 全球购物
SKECHERS斯凯奇中国官网:来自美国的运动休闲品牌
2018/11/14 全球购物
计算机专业自我鉴定
2013/10/15 职场文书
项目建议书范文
2014/05/12 职场文书
大学生标准自荐书
2014/06/15 职场文书
电子信息工程专业求职信
2014/06/28 职场文书
销售会议开幕词
2015/01/28 职场文书
十八大观后感
2015/06/12 职场文书
python中mongodb包操作数据库
2022/04/19 Python
Android Gradle 插件自定义Plugin实现注意事项
2022/06/16 Java/Android