通过实例解析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 分析Nginx访问日志并保存到MySQL数据库实例
Mar 13 Python
python计算对角线有理函数插值的方法
May 07 Python
Python实现的最近最少使用算法
Jul 10 Python
Python中read()、readline()和readlines()三者间的区别和用法
Jul 30 Python
浅谈python中字典append 到list 后值的改变问题
May 04 Python
django2用iframe标签完成网页内嵌播放b站视频功能
Jun 20 Python
python和shell监控linux服务器的详细代码
Jun 22 Python
windows下搭建python scrapy爬虫框架步骤
Dec 23 Python
Python字符串逆序的实现方法【一题多解】
Feb 18 Python
Python初学者常见错误详解
Jul 02 Python
Django使用中间键实现csrf认证详解
Jul 22 Python
如何完美的建立一个python项目
Oct 09 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调用三种数据库的方法(3)
2006/10/09 PHP
PHP 多维数组排序(usort,uasort)
2010/06/30 PHP
控制PHP的输出:缓存并压缩动态页面
2013/06/11 PHP
PHP实现生成唯一编号(36进制的不重复编号)
2014/07/01 PHP
PHP使用pear自带的mail类库发邮件的方法
2015/07/08 PHP
PHP实现webshell扫描文件木马的方法
2017/07/31 PHP
Prototype使用指南之form.js
2007/01/10 Javascript
js 匿名调用实现代码
2009/06/19 Javascript
在新窗口打开超链接的方法小结
2013/04/14 Javascript
javascript实现自动输出文本(打字特效)
2015/08/27 Javascript
Vue.js bootstrap前端实现分页和排序
2017/03/10 Javascript
Linux使用Node.js建立访问静态网页的服务实例详解
2017/03/21 Javascript
Javascript实现时间倒计时效果
2017/07/15 Javascript
代码详解JS操作剪贴板
2018/02/11 Javascript
Vue.js实现大转盘抽奖总结及实现思路
2019/10/09 Javascript
vue中实现高德定位功能
2019/12/03 Javascript
Vue-Ant Design Vue-普通及自定义校验实例
2020/10/24 Javascript
[01:21]DOTA2周边文化主题展 神秘商店火热开售
2017/07/30 DOTA
[01:00:14]DOTA2官方TI8总决赛纪录片 真视界True Sight
2019/01/16 DOTA
[56:13]DOTA2-DPC中国联赛定级赛 LBZS vs Phoenix BO3第一场 1月10日
2021/03/11 DOTA
Python使用函数默认值实现函数静态变量的方法
2014/08/18 Python
使用C语言来扩展Python程序和Zope服务器的教程
2015/04/14 Python
浅析Python中的多条件排序实现
2016/06/07 Python
你所不知道的Python奇技淫巧13招【实用】
2016/12/14 Python
Python使用cx_Oracle模块操作Oracle数据库详解
2018/05/07 Python
详解Pycharm出现out of memory的终极解决方法
2020/03/03 Python
Canvas获取视频第一帧缩略图的实现
2020/11/11 HTML / CSS
Cole Haan官方网站:美国时尚潮流品牌
2017/12/06 全球购物
全球工业:Global Industrial
2020/02/01 全球购物
J2EE模式面试题
2016/10/11 面试题
小学生家长评语大全
2014/02/10 职场文书
科级干部考察材料
2014/02/15 职场文书
销售人员工作自我评价
2014/09/21 职场文书
个人查摆问题整改措施
2014/10/04 职场文书
总账会计岗位职责
2015/04/02 职场文书
golang 如何用反射reflect操作结构体
2021/04/28 Golang