通过实例解析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 tkinter模块弹出窗口及传值回到主窗口操作详解
Jul 28 Python
python距离测量的方法
Mar 06 Python
Python 读取图片文件为矩阵和保存矩阵为图片的方法
Apr 27 Python
python实现批量修改图片格式和尺寸
Jun 07 Python
Python双向循环链表实现方法分析
Jul 30 Python
Python简单获取二维数组行列数的方法示例
Dec 21 Python
Python实现网站表单提交和模板
Jan 15 Python
python实现QQ空间自动点赞功能
Apr 09 Python
对Django 中request.get和request.post的区别详解
Aug 12 Python
Django admin.py 在修改/添加表单界面显示额外字段的方法
Aug 22 Python
想学画画?python满足你!
Dec 24 Python
python实现马丁策略的实例详解
Jan 15 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实现cc攻击防御和防止快速刷新页面示例
2014/02/13 PHP
使用PHP生成二维码的两种方法(带logo图像)
2014/03/14 PHP
Symfony控制层深入详解
2016/03/17 PHP
php实现解析xml并生成sql语句的方法
2018/02/03 PHP
Thinkphp5框架ajax接口实现方法分析
2019/08/28 PHP
Display SQL Server Login Mode
2007/06/21 Javascript
Extjs 几个方法的讨论
2010/01/28 Javascript
js操作table示例(个人心得)
2013/11/29 Javascript
Javascript selection的兼容性写法介绍
2013/12/20 Javascript
JS不间断向上滚动效果代码
2013/12/25 Javascript
js通过八个点 拖动改变div大小的实现方法
2014/03/05 Javascript
js改变鼠标的形状和样式的方法
2014/03/31 Javascript
Extjs Label的 fieldLabel和html属性值对齐的方法
2014/06/15 Javascript
如何正确使用javascript 来进行我们的程序开发
2014/06/23 Javascript
jQuery实现ichat在线客服插件
2014/12/29 Javascript
JavaScript学习笔记之DOM基础 2.4
2015/08/14 Javascript
如何使用headjs来管理和异步加载js
2016/11/29 Javascript
js实现前端分页页码管理
2017/01/06 Javascript
微信小程序遇到修改数据后页面不渲染的问题解决
2017/03/09 Javascript
详解Vue-cli 创建的项目如何跨域请求
2017/05/18 Javascript
解决前后端分离 vue+springboot 跨域 session+cookie失效问题
2019/05/13 Javascript
基于Vue实现平滑过渡的拖拽排序功能
2019/06/12 Javascript
p5.js临摹动态图形的方法
2019/10/23 Javascript
[01:14:31]Secret vs VG 2018国际邀请赛淘汰赛BO3 第一场 8.23
2018/08/24 DOTA
python如何爬取个性签名
2018/06/19 Python
Django项目中添加ldap登陆认证功能的实现
2019/04/04 Python
Python 爬取淘宝商品信息栏目的实现
2021/02/06 Python
全球速卖通:AliExpress(国际版淘宝)
2017/09/20 全球购物
基督教卡片、励志礼品、家居装饰等:DaySpring
2018/10/12 全球购物
Pretty You London官网:英国拖鞋和睡衣品牌
2019/05/08 全球购物
请用用Java代码写一个堆栈
2012/01/26 面试题
nohup的用法
2012/11/26 面试题
区域销售经理职责
2013/12/22 职场文书
中秋节慰问信
2015/02/15 职场文书
工作会议通知
2015/04/15 职场文书
MySQL之MyISAM存储引擎的非聚簇索引详解
2022/03/03 MySQL