通过实例解析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最基本的数据类型以及对元组的介绍
Apr 14 Python
Python实现登录人人网并抓取新鲜事的方法
May 11 Python
Python3实现的画图及加载图片动画效果示例
Jan 19 Python
Python爬虫使用脚本登录Github并查看信息
Jul 16 Python
python利用多种方式来统计词频(单词个数)
May 27 Python
Python GUI编程 文本弹窗的实例
Jun 11 Python
Django使用模板后无法找到静态资源文件问题解决
Jul 19 Python
Python搭建代理IP池实现存储IP的方法
Oct 27 Python
TFRecord文件查看包含的所有Features代码
Feb 17 Python
解决pymysql cursor.fetchall() 获取不到数据的问题
May 15 Python
python super()函数的基本使用
Sep 10 Python
python 爬虫基本使用——统计杭电oj题目正确率并排序
Oct 26 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 图像处理类1
2009/06/15 PHP
PHP使用正则表达式清除超链接文本
2013/11/12 PHP
Zend Guard使用指南及问题处理
2015/01/07 PHP
PHP伪造来源HTTP_REFERER的方法实例详解
2015/07/06 PHP
如何把php5.3版本升级到php5.4或者php5.5
2015/07/31 PHP
thinkPHP5框架auth权限控制类与用法示例
2018/06/12 PHP
laravel-admin自动生成模块,及相关基础配置方法
2019/10/08 PHP
jquery $(document).ready() 与window.onload的区别
2009/12/28 Javascript
JavaScript 学习笔记(七)字符串的连接
2009/12/31 Javascript
Jquery中getJSON在asp.net中的使用说明
2011/03/10 Javascript
JS遍历ul下的li点击弹出li的索引的实现方法
2016/09/19 Javascript
原生js图片轮播效果实现代码
2016/10/19 Javascript
JavaScript实现星星等级评价功能
2017/03/22 Javascript
详解使用fetch发送post请求时的参数处理
2017/04/05 Javascript
详解angular 中的自定义指令之详解API
2017/06/20 Javascript
基于vue.js中事件修饰符.self的用法(详解)
2018/02/23 Javascript
JS为什么说async/await是generator的语法糖详解
2019/07/11 Javascript
js实现抽奖的两种方法
2020/03/19 Javascript
实例讲解React 组件
2020/07/07 Javascript
vue实现多个echarts根据屏幕大小变化而变化实例
2020/07/19 Javascript
[01:13:59]LGD vs Mineski Supermajor 胜者组 BO3 第三场 6.5
2018/06/06 DOTA
[05:46]2018完美盛典-《同梦共竞》
2018/12/17 DOTA
Python下的常用下载安装工具pip的安装方法
2015/11/13 Python
关于Python中异常(Exception)的汇总
2017/01/18 Python
python对DICOM图像的读取方法详解
2017/07/17 Python
python提取包含关键字的整行数据方法
2018/12/11 Python
postman模拟访问具有Session的post请求方法
2019/07/15 Python
Django发送邮件和itsdangerous模块的配合使用解析
2019/08/10 Python
解决Python import docx出错DLL load failed的问题
2020/02/13 Python
详解Python 循环嵌套
2020/07/09 Python
html+js 实现markdown编辑器效果
2019/10/23 HTML / CSS
世界上最大的隐形眼镜商店:1-800 Contacts
2018/11/03 全球购物
爱国口号
2014/06/19 职场文书
2014年小学教师工作总结
2014/11/10 职场文书
2016年党风廉政建设承诺书
2016/03/25 职场文书
辞职信怎么写?
2019/05/21 职场文书