通过实例解析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的Django框架中的数据库配置指南
Jul 17 Python
django用户注册、登录、注销和用户扩展的示例
Mar 19 Python
Python 修改列表中的元素方法
Jun 26 Python
Python实现矩阵相乘的三种方法小结
Jul 26 Python
Python实现全排列的打印
Aug 18 Python
python得到一个excel的全部sheet标签值方法
Dec 10 Python
在Python中,不用while和for循环遍历列表的实例
Feb 20 Python
Python使用统计函数绘制简单图形实例代码
May 15 Python
关于django 1.10 CSRF验证失败的解决方法
Aug 31 Python
python绘制规则网络图形实例
Dec 09 Python
python 如何将office文件转换为PDF
Sep 22 Python
sublime3之内网安装python插件Anaconda的流程
Nov 10 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运行模式汇总
2016/11/06 PHP
PHP程序员简单的开展服务治理架构操作详解(一)
2020/05/14 PHP
js操作iframe的一些方法介绍
2013/06/25 Javascript
Javasipt:操作radio标签详解
2013/12/30 Javascript
jquery中$(#form :input)与$(#form input)的区别
2014/08/18 Javascript
JavaScript bold方法入门实例(把指定文字显示为粗体)
2014/10/17 Javascript
PHP 数组current和next用法分享
2015/03/05 Javascript
javascript下使用Promise封装FileReader
2016/02/19 Javascript
修改js confirm alert 提示框文字的简单实例
2016/06/10 Javascript
js中常用的Tab切换效果(推荐)
2016/08/30 Javascript
利用vue写todolist单页应用
2016/12/15 Javascript
JavaScript优化以及前段开发小技巧
2017/02/02 Javascript
JavaScript实现的DOM绘制柱状图效果示例
2018/08/08 Javascript
Node.js原生api搭建web服务器的方法步骤
2019/02/15 Javascript
详解VSCode配置启动Vue项目
2019/05/14 Javascript
Vue实现商品飞入购物车效果(电商项目)
2019/11/26 Javascript
vue实现淘宝购物车功能
2020/04/20 Javascript
webpack 如何同时输出压缩和未压缩的文件的实现步骤
2020/06/05 Javascript
Python实现的Google IP 可用性检测脚本
2015/04/23 Python
python回调函数用法实例分析
2015/05/09 Python
Python实现对百度云的文件上传(实例讲解)
2017/10/21 Python
利用scrapy将爬到的数据保存到mysql(防止重复)
2018/03/31 Python
Python读取csv文件分隔符设置方法
2019/01/14 Python
python print出共轭复数的方法详解
2019/06/25 Python
简单了解python gevent 协程使用及作用
2019/07/22 Python
Django实现celery定时任务过程解析
2020/04/21 Python
python 实现性别识别
2020/11/21 Python
艺术用品:Arteza
2018/11/25 全球购物
mysql_pconnect()和mysql_connect()有什么区别
2012/05/25 面试题
消防安全检查制度
2014/02/04 职场文书
《去年的树》教学反思
2014/04/11 职场文书
暖通工程师岗位职责
2014/06/12 职场文书
小学秋季运动会报道稿
2014/09/30 职场文书
童年读书笔记
2015/06/26 职场文书
Vue提供的三种调试方式你知道吗
2022/01/18 Vue.js
Python 装饰器(decorator)常用的创建方式及解析
2022/04/24 Python