通过实例解析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的dict,set,list,tuple应用详解
Jul 24 Python
Python实现读取邮箱中的邮件功能示例【含文本及附件】
Aug 05 Python
python实现多人聊天室
Mar 31 Python
Python基础学习之基本数据结构详解【数字、字符串、列表、元组、集合、字典】
Jun 18 Python
python爬虫解决验证码的思路及示例
Aug 01 Python
TensorFlow学习之分布式的TensorFlow运行环境
Feb 05 Python
使用Python求解带约束的最优化问题详解
Feb 11 Python
python数据预处理 :数据共线性处理详解
Feb 24 Python
Python之关于类变量的两种赋值区别详解
Mar 12 Python
解决pip安装的第三方包在PyCharm无法导入的问题
Oct 15 Python
Pytorch 图像变换函数集合小结
Feb 01 Python
python中的被动信息搜集
Apr 29 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程序员应该了解MongoDB的五件事
2013/06/03 PHP
php 备份数据库代码(生成word,excel,json,xml,sql)
2013/06/23 PHP
必须收藏的23个php实用代码片段
2016/02/02 PHP
PHP编写daemon process详解及实例代码
2016/09/30 PHP
PHP网站自动化配置的实现方法(必看)
2017/05/27 PHP
thinkphp5 模型实例化获得数据对象的教程
2019/10/18 PHP
flexigrid 参数说明
2010/11/23 Javascript
JS高级笔记
2011/07/13 Javascript
为JavaScript类型增加方法的实现代码(增加功能)
2011/12/29 Javascript
localResizeIMG先压缩后使用ajax无刷新上传(移动端)
2015/08/11 Javascript
jQuery简单实现提交数据出现loading进度条的方法
2016/03/29 Javascript
通过扫描二维码打开app的实现代码
2016/11/10 Javascript
require、backbone等重构手机图片查看器
2016/11/17 Javascript
JS中跳出循环的示例代码
2017/09/14 Javascript
原生JS实现网页手机音乐播放器 歌词同步播放的示例
2018/02/02 Javascript
ES6下子组件调用父组件的方法(推荐)
2018/02/23 Javascript
angularjs select 赋值 ng-options配置方法
2018/02/28 Javascript
浅谈AngularJS中$http服务的简单用法
2018/05/15 Javascript
vue mounted 调用两次的完美解决办法
2018/10/29 Javascript
15分钟深入了解JS继承分类、原理与用法
2019/01/19 Javascript
JavaScript深入V8引擎以及编写优化代码的5个技巧
2019/06/24 Javascript
原生javascript如何实现共享onload事件
2020/07/03 Javascript
一篇文章入门Python生态系统(Python新手入门指导)
2015/12/11 Python
python实现猜数字小游戏
2020/03/24 Python
如何在vscode中安装python库的方法步骤
2021/01/06 Python
CSS3 渐变(Gradients)之CSS3 线性渐变
2016/07/08 HTML / CSS
学校办公室主任职责
2013/12/27 职场文书
生产车间主管岗位职责
2013/12/28 职场文书
竞聘上岗演讲稿范文
2014/01/10 职场文书
社会实践感言
2014/01/25 职场文书
关爱残疾人标语
2014/06/25 职场文书
师范生免费教育协议书范本
2014/10/09 职场文书
中学生的1000字检讨书
2014/10/11 职场文书
2016年度师德标兵先进事迹材料
2016/02/26 职场文书
Go语言带缓冲的通道实现
2021/04/26 Golang
四十九个javascript小知识实用技巧
2021/11/20 Javascript