通过实例解析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 crontab设置linux定时任务
Dec 08 Python
Python中查看文件名和文件路径
Mar 31 Python
Python中扩展包的安装方法详解
Jun 14 Python
python中判断文件编码的chardet(实例讲解)
Dec 21 Python
Python使用Pickle库实现读写序列操作示例
Jun 15 Python
djang常用查询SQL语句的使用代码
Feb 15 Python
Python用字典构建多级菜单功能
Jul 11 Python
python实现XML解析的方法解析
Nov 16 Python
python3实现elasticsearch批量更新数据
Dec 03 Python
Python类反射机制使用实例解析
Dec 30 Python
关于pytorch处理类别不平衡的问题
Dec 31 Python
PyQt5 界面显示无响应的实现
Mar 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实现的sqlite数据库连接类
2014/12/12 PHP
Symfony2学习笔记之插件格式分析
2016/03/17 PHP
PHP利用Socket获取网站的SSL证书与公钥
2017/06/18 PHP
php实现生成带二维码图片并强制下载功能
2018/02/24 PHP
php根据命令行参数生成配置文件详解
2019/03/15 PHP
Laravel 解决composer相关操作提示php相关异常的问题
2019/10/23 PHP
(JS实现)MapBar中坐标的加密和解密的脚本
2007/05/16 Javascript
js字符串的各种格式的转换 ToString,Format
2011/08/08 Javascript
html中table数据排序的js代码
2011/08/09 Javascript
JS判断两个时间大小的示例代码
2014/01/28 Javascript
javascript将浮点数转换成整数的三个方法
2014/06/23 Javascript
基于jQuery实现的图片切换焦点图整理
2014/12/07 Javascript
JS+CSS实现可拖动的弹出提示框
2015/02/16 Javascript
javascript实现简单的进度条
2015/07/02 Javascript
jQuery密码强度检测插件passwordStrength用法实例分析
2015/10/30 Javascript
JavaScript为事件句柄绑定监听函数实例详解
2015/12/15 Javascript
jquery对dom节点的操作【推荐】
2016/04/15 Javascript
拥Bootstrap入怀——导航栏篇
2016/05/30 Javascript
jquery 正整数数字校验正则表达式
2017/01/10 Javascript
AngularJS+bootstrap实现动态选择商品功能示例
2017/05/17 Javascript
Easyui和zTree两种方式分别实现树形下拉框
2017/08/04 Javascript
React-Native使用Mobx实现购物车功能
2017/09/14 Javascript
微信小程序倒计时功能实现代码
2017/11/09 Javascript
windows下更新npm和node的方法
2017/11/30 Javascript
微信小程序 MinUI组件库系列之badge徽章组件示例
2018/08/20 Javascript
微信小程序MUI导航栏透明渐变功能示例(通过改变opacity实现)
2019/01/24 Javascript
微信小程序引入模块中wxml、wxss、js的方法示例
2019/08/09 Javascript
vue el-table实现自定义表头
2019/12/11 Javascript
微信小程序绘制半圆(弧形)进度条
2020/11/18 Javascript
pycharm+django创建一个搜索网页实例代码
2018/01/24 Python
Python统计python文件中代码,注释及空白对应的行数示例【测试可用】
2018/07/25 Python
python字典key不能是可以是啥类型
2020/08/04 Python
波兰品牌内衣及泳装网上商店:Astratex.pl
2017/02/03 全球购物
党员“四风”问题批评与自我批评思想汇报
2014/10/06 职场文书
如何利用JavaScript实现二叉搜索树
2021/04/02 Javascript
Mysql - 常用函数 每天积极向上
2021/04/05 MySQL