通过实例解析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 相关文章推荐
写了个监控nginx进程的Python脚本
May 10 Python
python计算时间差的方法
May 20 Python
Flask框架web开发之零基础入门
Dec 10 Python
python使用wxpy实现微信消息防撤回脚本
Apr 29 Python
使用Python实现企业微信的自动打卡功能
Apr 30 Python
Django项目主urls导入应用中views的红线问题解决
Aug 10 Python
Python 实现黑客帝国中的字符雨的示例代码
Feb 20 Python
利用Python实现朋友圈中的九宫格图片效果
Sep 03 Python
python批量修改交换机密码的示例
Sep 22 Python
python 还原梯度下降算法实现一维线性回归
Oct 22 Python
如何用python批量调整视频声音
Dec 22 Python
Python list列表删除元素的4种方法
Nov 01 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 操作调试的方法
2012/07/12 PHP
Linux下实现PHP多进程的方法分享
2012/08/16 PHP
XAMPP安装与使用方法详细解析
2013/11/27 PHP
PHP实现图的邻接矩阵表示及几种简单遍历算法分析
2017/11/24 PHP
php面试中关于面向对象的相关问题
2019/02/13 PHP
TNC vs BOOM BO3 第一场2.13
2021/03/10 DOTA
pjblog中的UBBCode.js
2007/04/25 Javascript
Javascript技巧之不要用for in语句对数组进行遍历
2010/10/20 Javascript
Jquery图形报表插件 jqplot简介及参数详解
2012/10/10 Javascript
一个JavaScript函数把URL参数解析成Json对象
2014/09/24 Javascript
JavaScript实现Flash炫光波动特效
2015/05/14 Javascript
javascript中substring()、substr()、slice()的区别
2015/08/30 Javascript
JS实现网页上随机产生超链接地址的方法
2015/11/09 Javascript
详解react-router如何实现按需加载
2017/06/15 Javascript
ReactNative实现图片上传功能的示例代码
2017/07/11 Javascript
JS获取填报扩展单元格控件的值的解决办法
2017/07/14 Javascript
javascript 中事件冒泡和事件捕获机制的详解
2017/09/01 Javascript
vue学习笔记之v-if和v-show的区别
2017/09/20 Javascript
如何解决js函数防抖、节流出现的问题
2019/06/17 Javascript
Layui选项卡制作历史浏览记录的方法
2019/09/28 Javascript
es5 类与es6中class的区别小结
2020/11/09 Javascript
python中redis的安装和使用
2016/12/04 Python
python密码错误三次锁定(实例讲解)
2017/11/14 Python
Python对List中的元素排序的方法
2018/04/01 Python
怎样建立和理解非常复杂的声明?例如定义一个包含N 个指向返回 指向字符的指针的函数的指针的数组?
2013/03/19 面试题
英文留学推荐信范文
2014/01/25 职场文书
社会学专业学生职业规划书
2014/02/07 职场文书
学雷锋宣传标语
2014/06/25 职场文书
2014年十八届四中全会思想汇报范文
2014/10/17 职场文书
旷工检讨书1000字
2015/01/01 职场文书
教师个人年度总结
2015/02/11 职场文书
开学典礼校长致辞
2015/07/29 职场文书
导游词之张家界
2019/10/31 职场文书
SQL IDENTITY_INSERT作用案例详解
2021/08/23 MySQL
口袋妖怪冰系十大最强精灵,几何雪花排第七,第六类似北极熊
2022/03/18 日漫
win10频率超出范围怎么办?win10老显示超出工作频率范围的解决方法
2022/07/07 数码科技