通过实例解析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中实现k-means聚类算法详解
Nov 11 Python
好的Python培训机构应该具备哪些条件
May 23 Python
matplotlib savefig 保存图片大小的实例
May 24 Python
Python Learning 列表的更多操作及示例代码
Aug 22 Python
python网络应用开发知识点浅析
May 28 Python
python 消除 futureWarning问题的解决
Dec 25 Python
keras 特征图可视化实例(中间层)
Jan 24 Python
python add_argument()用法解析
Jan 29 Python
python标准库os库的函数介绍
Feb 12 Python
Python如何将将模块分割成多个文件
Aug 04 Python
基于Pytorch版yolov5的滑块验证码破解思路详解
Feb 25 Python
Python内置类型集合set和frozenset的使用详解
Apr 26 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
兼容firefox,chrome的网页灰度效果
2011/08/08 PHP
采用CSS和JS,刚好我最近有个站点要用到下拉菜单!
2006/06/26 Javascript
关于javascript DOM事件模型的两件事
2010/07/22 Javascript
用方法封装javascript的new操作符(一)
2010/12/25 Javascript
Jquery之Ajax运用 学习运用篇
2011/09/26 Javascript
验证码按回车不变解决方法
2013/03/29 Javascript
jquery ajax方式直接提交整个表单核心代码
2013/08/15 Javascript
Jquery选中或取消radio示例
2013/09/29 Javascript
jQuery子属性过滤选择器用法分析
2015/02/10 Javascript
javascript闭包概念简单解析(推荐)
2016/06/03 Javascript
基于JS如何实现类似QQ好友头像hover时显示资料卡的效果(推荐)
2016/06/09 Javascript
javascript中的 object 和 function小结
2016/08/14 Javascript
有趣的bootstrap走动进度条
2016/12/01 Javascript
浅析javascript中的Event事件
2016/12/09 Javascript
BootStrap Table复选框默认选中功能的实现代码(从数据库获取到对应的状态进行判断是否为选中状态)
2017/07/11 Javascript
React 子组件向父组件传值的方法
2017/07/24 Javascript
node作为中间服务层如何发送请求(发送请求的实现方法详解)
2018/01/02 Javascript
解决低版本的浏览器不支持es6的import问题
2018/03/09 Javascript
详解vue 计算属性与方法跟侦听器区别(面试考点)
2018/04/23 Javascript
iconfont的三种使用方式详解
2018/08/05 Javascript
js中innerText/textContent和innerHTML与target和currentTarget的区别
2019/01/21 Javascript
react写一个select组件的实现代码
2019/04/03 Javascript
JS正则表达式常见函数与用法小结
2020/04/13 Javascript
vue输入框使用模糊搜索功能的实现代码
2020/05/26 Javascript
微信小程序实现导航栏和内容上下联动功能代码
2020/06/29 Javascript
JavaScript如何判断对象有某属性
2020/07/03 Javascript
vue接通后端api以及部署到服务器操作
2020/08/13 Javascript
Openlayers测量距离与面积的实现方法
2020/09/25 Javascript
python生成指定尺寸缩略图的示例
2014/05/07 Python
在Python的Flask框架中验证注册用户的Email的方法
2015/09/02 Python
python中时间、日期、时间戳的转换的实现方法
2019/07/06 Python
pycharm配置安装autopep8自动规范代码的实现
2021/03/02 Python
小学毕业感言500字
2014/02/28 职场文书
教师考核评语
2014/04/28 职场文书
初二学生评语大全
2014/12/26 职场文书
Spring Boot实战解决高并发数据入库之 Redis 缓存+MySQL 批量入库问题
2022/02/12 Redis