通过实例解析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操作MySQL的一些基本方法
Aug 16 Python
python3 拼接字符串的7种方法
Sep 12 Python
python使用多进程的实例详解
Sep 19 Python
Python list列表中删除多个重复元素操作示例
Feb 27 Python
对python中的控制条件、循环和跳出详解
Jun 24 Python
使用浏览器访问python写的服务器程序
Oct 10 Python
python基于property()函数定义属性
Jan 22 Python
Pytorch转onnx、torchscript方式
May 25 Python
python中return不返回值的问题解析
Jul 22 Python
基于django和dropzone.js实现上传文件
Nov 24 Python
OpenCV+python实现膨胀和腐蚀的示例
Dec 21 Python
python中re模块知识点总结
Jan 17 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 数组遍历顺序理解
2009/09/09 PHP
采用PHP函数memory_get_usage获取PHP内存清耗量的方法
2011/12/06 PHP
php session劫持和防范的方法
2013/11/12 PHP
PHP 生成N个不重复的随机数
2015/01/21 PHP
Discuz论坛密码与密保加密规则
2016/12/19 PHP
PHP数组操作实例分析【添加,删除,计算,反转,排序,查找等】
2016/12/24 PHP
PHP基于关联数组20行代码搞定约瑟夫问题示例
2017/11/07 PHP
Laravel下生成验证码的类
2017/11/15 PHP
php 截取中英文混合字符串的方法
2018/05/31 PHP
php的无刷新操作实现方法分析
2020/02/28 PHP
PHP设计模式(三)建造者模式Builder实例详解【创建型】
2020/05/02 PHP
javascript 学习笔记(一)DOM基本操作
2011/04/08 Javascript
现如今最流行的JavaScript代码规范
2014/03/08 Javascript
详解javascript中的事件处理
2015/11/06 Javascript
微信小程序 获取微信OpenId详解及实例代码
2016/10/31 Javascript
前端MVVM框架解析之双向绑定
2018/01/24 Javascript
性能优化篇之Webpack构建速度优化的建议
2019/04/03 Javascript
老生常谈Python之装饰器、迭代器和生成器
2017/07/26 Python
TensorFlow 滑动平均的示例代码
2018/06/19 Python
python实现知乎高颜值图片爬取
2019/08/12 Python
Python SSL证书验证问题解决方案
2020/01/13 Python
Python web如何在IIS发布应用过程解析
2020/05/27 Python
基于SQLAlchemy实现操作MySQL并执行原生sql语句
2020/06/10 Python
详解移动端HTML5音频与视频问题及解决方案
2018/08/22 HTML / CSS
利用纯html5绘制出来的一款非常漂亮的时钟
2015/01/04 HTML / CSS
英国知名衬衫品牌美国网站:Charles Tyrwhitt美国
2016/08/28 全球购物
Deichmann英国:德国鞋类零售商
2021/01/30 全球购物
大学生护理专业自荐信
2013/10/03 职场文书
师范生实习自我鉴定
2013/11/01 职场文书
产品销售员岗位职责
2013/12/18 职场文书
培训协议书范本
2014/04/22 职场文书
《水乡歌》教学反思
2014/04/24 职场文书
北京奥运会主题口号
2014/06/13 职场文书
社区创先争优承诺书
2014/08/30 职场文书
拔河比赛队名及霸气口号
2015/12/24 职场文书
mysql连接查询中and与where的区别浅析
2021/07/01 MySQL