通过实例解析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时间整形转标准格式的示例分享
Feb 14 Python
Python定时执行之Timer用法示例
May 27 Python
Django视图和URL配置详解
Jan 31 Python
python实现Decorator模式实例代码
Feb 09 Python
Python中矩阵创建和矩阵运算方法
Aug 04 Python
Python判断对象是否为文件对象(file object)的三种方法示例
Apr 26 Python
Python数据类型之Set集合实例详解
May 07 Python
如何使用python操作vmware
Jul 27 Python
python子线程退出及线程退出控制的代码
Oct 16 Python
Python使用sqlite3模块内置数据库
May 07 Python
python Matplotlib基础--如何添加文本和标注
Jan 26 Python
Django给表单添加honeypot验证增加安全性
May 06 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提取视频网站页面中的FLASH地址的代码
2010/04/17 PHP
PHP获取浏览器信息类和客户端地理位置的2个方法
2014/04/24 PHP
php文件缓存类汇总
2014/11/21 PHP
PHP中filter函数校验数据的方法详解
2015/07/31 PHP
jQuery动态添加的元素绑定事件处理函数代码
2011/08/02 Javascript
addEventListener()第三个参数useCapture (Boolean)详细解析
2013/11/07 Javascript
使用不同的方法结合/合并两个JS数组
2014/09/18 Javascript
jquery实现简单的二级导航下拉菜单效果
2015/09/07 Javascript
基于JavaScript如何实现ajax调用后台定义的方法
2015/12/29 Javascript
JavaScript事件处理的方式(三种)
2016/04/26 Javascript
浅谈js中的延迟执行和定时执行
2016/05/31 Javascript
微信js-sdk界面操作接口用法示例
2016/10/12 Javascript
jQuery代码实现实时获取时间
2017/01/29 Javascript
Vue.js基础学习之class与样式绑定
2017/03/20 Javascript
js实现一个猜数字游戏
2017/03/31 Javascript
Vue2.0表单校验组件vee-validate的使用详解
2017/05/02 Javascript
JavaScript实现QQ聊天消息展示和评论提交功能
2017/05/22 Javascript
详解Axios 如何取消已发送的请求
2018/10/20 Javascript
详解如何使用nvm管理Node.js多版本
2019/05/06 Javascript
微信小程序实现Session功能及无法获取session问题的解决方法
2019/05/07 Javascript
layui实现数据表格自定义数据项
2019/10/26 Javascript
Python 可爱的大小写
2008/09/06 Python
在Python的Bottle框架中使用微信API的示例
2015/04/23 Python
Python中Django框架下的staticfiles使用简介
2015/05/30 Python
手把手教你python实现SVM算法
2017/12/27 Python
selenium + python 获取table数据的示例讲解
2018/10/13 Python
Python中typing模块与类型注解的使用方法
2019/08/05 Python
Python OpenCV实现鼠标画框效果
2020/08/19 Python
Python使用循环神经网络解决文本分类问题的方法详解
2020/01/16 Python
python实现提取COCO,VOC数据集中特定的类
2020/03/10 Python
移动端HTML5开发神器之vconsole详解
2020/12/15 HTML / CSS
电子专业求职信
2014/06/19 职场文书
幼儿园卫生保健制度
2015/08/05 职场文书
Redis基于Bitmap实现用户签到功能
2021/06/20 Redis
Html5获取用户当前位置的几种方式
2022/01/18 HTML / CSS
Vue如何清空对象
2022/03/03 Vue.js