通过实例解析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 文件操作技巧(File operation) 实例代码分析
Aug 11 Python
python实现rest请求api示例
Apr 22 Python
在Python中使用SQLite的简单教程
Apr 29 Python
Python简单实现Base64编码和解码的方法
Apr 29 Python
Python装饰器知识点补充
May 28 Python
Django forms组件的使用教程
Oct 08 Python
python实现全盘扫描搜索功能的方法
Feb 14 Python
Python中生成一个指定长度的随机字符串实现示例
Nov 06 Python
关于Tensorflow使用CPU报错的解决方式
Feb 05 Python
python+opencv边缘提取与各函数参数解析
Mar 09 Python
Python 3.9的到来到底是意味着什么
Oct 14 Python
基于Python的图像阈值化分割(迭代法)
Nov 20 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的PDO预定义常量讲解
2019/01/24 PHP
[原创]网络复制内容时常用的正则+editplus
2006/11/30 Javascript
单独使用CKFinder选择图片的方法
2010/08/21 Javascript
基于JavaScript 声明全局变量的三种方式详解
2013/05/07 Javascript
把文本中的URL地址转换为可点击链接的JavaScript、PHP自定义函数
2014/07/29 Javascript
基于javascript如何传递特殊字符
2015/11/30 Javascript
不能不知道的10个angularjs英文学习网站
2016/03/23 Javascript
使用jQuery Rotare实现微信大转盘抽奖功能
2016/06/20 Javascript
jQuery插件扩展测试实例
2016/06/21 Javascript
使用smartupload组件实现jsp+jdbc上传下载文件实例解析
2017/01/05 Javascript
微信小程序开发之Tabbar实例详解
2017/01/09 Javascript
详解Angular.js数据绑定时自动转义html标签及内容
2017/03/30 Javascript
JavaScript之面向对象_动力节点Java学院整理
2017/06/29 Javascript
JS+Ajax实现百度智能搜索框
2017/08/04 Javascript
关于redux-saga中take使用方法详解
2018/02/27 Javascript
electron + vue项目实现打印小票功能及实现代码
2018/11/25 Javascript
VUE简单的定时器实时刷新的实现方法
2019/01/20 Javascript
Vue加载json文件的方法简单示例
2019/01/28 Javascript
简单了解vue中父子组件如何相互传递值(基础向)
2019/07/12 Javascript
vscode调试node.js的实现方法
2020/03/22 Javascript
vue使用nprogress加载路由进度条的方法
2020/06/04 Javascript
python常规方法实现数组的全排列
2015/03/17 Python
python中装饰器级连的使用方法示例
2017/09/29 Python
Django使用paginator插件实现翻页功能的实例
2018/10/24 Python
python与字符编码问题
2019/05/24 Python
Python 虚拟空间的使用代码详解
2019/06/10 Python
python tornado修改log输出方式
2019/11/18 Python
pytorch 移动端部署之helloworld的使用
2020/10/30 Python
Django配置跨域并开发测试接口
2020/11/04 Python
香港卓悦化妆品官网:BONJOUR
2017/09/21 全球购物
司马光教学反思
2014/02/01 职场文书
财务科科长岗位职责
2014/03/10 职场文书
2014年德育工作总结
2014/11/20 职场文书
2014年机关工会工作总结
2014/12/19 职场文书
导游词之无锡古运河
2019/11/14 职场文书
MySQL 查询速度慢的原因
2021/05/25 MySQL