通过实例解析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中encode()方法的使用简介
May 18 Python
玩转python爬虫之URLError异常处理
Feb 17 Python
Python+Selenium+PIL+Tesseract自动识别验证码进行一键登录
Sep 20 Python
python 中的divmod数字处理函数浅析
Oct 17 Python
python执行使用shell命令方法分享
Nov 08 Python
Python解析并读取PDF文件内容的方法
May 08 Python
Python机器学习库scikit-learn安装与基本使用教程
Jun 25 Python
python找出完数的方法
Nov 12 Python
windows上彻底删除jupyter notebook的实现
Apr 13 Python
Python telnet登陆功能实现代码
Apr 16 Python
如何使用 Python 读取文件和照片的创建日期
Sep 05 Python
pycharm部署django项目到云服务器的详细流程
Jun 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 301转向实现代码
2008/09/18 PHP
一个经典实用的PHP图像处理类分享
2014/11/18 PHP
PHP两种实现无级递归分类的方法
2017/03/02 PHP
PHP实现的防止跨站和xss攻击代码【来自阿里云】
2018/01/29 PHP
可以显示单图片,多图片ajax请求的ThickBox3.1类下载
2007/12/23 Javascript
JavaScript入门教程 Cookies
2009/01/31 Javascript
javascript 继承实现方法
2009/08/26 Javascript
Jquery ajax不能解析json对象,报Invalid JSON错误的原因和解决方法
2010/03/27 Javascript
你必须知道的Javascript知识点之"深入理解作用域链"的介绍
2013/04/23 Javascript
JavaScript代码实现图片循环滚动效果
2020/03/19 Javascript
微信小程序 地图定位简单实例
2016/10/14 Javascript
手动用webpack搭建第一个ReactApp的示例
2018/04/11 Javascript
在Create React App中使用CSS Modules的方法示例
2019/01/15 Javascript
node链接mongodb数据库的方法详解【阿里云服务器环境ubuntu】
2019/03/07 Javascript
layer弹出层扩展主题的方法
2019/09/11 Javascript
Vue将props值实时传递 并可修改的操作
2020/08/09 Javascript
Python中字符编码简介、方法及使用建议
2015/01/08 Python
python实现下载整个ftp目录的方法
2017/01/17 Python
Python3多线程爬虫实例讲解代码
2018/01/05 Python
python 爬虫 批量获取代理ip的实例代码
2018/05/22 Python
如何不用安装python就能在.NET里调用Python库
2019/07/12 Python
Python如何实现动态数组
2019/11/02 Python
pyinstaller还原python代码过程图解
2020/01/08 Python
python实现简单井字棋小游戏
2020/03/05 Python
Python并发请求下限制QPS(每秒查询率)的实现代码
2020/06/05 Python
深入了解Python enumerate和zip
2020/07/16 Python
python 如何区分return和yield
2020/09/22 Python
专门出售各种儿童读物的网站:Put Me In The Story
2016/08/07 全球购物
植物选择:Botanic Choice
2017/02/15 全球购物
写一个函数返回1+2+3+…+n的值(假定结果不会超过长整型变量的范围)
2014/09/05 面试题
Linux操作面试题
2015/02/11 面试题
新学期红领巾广播稿
2014/01/14 职场文书
劳资协议书范本
2014/04/23 职场文书
学习退步检讨书
2014/09/28 职场文书
2016教师学习党章心得体会
2016/01/15 职场文书
Jmerte 分布式压测及分布式压测配置
2022/04/30 Java/Android