通过实例解析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之关于循环的小伎俩
Oct 02 Python
python通过imaplib模块读取gmail里邮件的方法
May 08 Python
Python实现将16进制字符串转化为ascii字符的方法分析
Jul 21 Python
python使用pil库实现图片合成实例代码
Jan 20 Python
使用python将excel数据导入数据库过程详解
Aug 27 Python
将python包发布到PyPI和制作whl文件方式
Dec 25 Python
PyTorch 解决Dataset和Dataloader遇到的问题
Jan 08 Python
Python while循环使用else语句代码实例
Feb 07 Python
解析pip安装第三方库但PyCharm中却无法识别的问题及PyCharm安装第三方库的方法教程
Mar 10 Python
keras小技巧——获取某一个网络层的输出方式
May 23 Python
QML实现钟表效果
Jun 02 Python
关于Theano和Tensorflow多GPU使用问题
Jun 19 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 JS Ip地址及域名格式检测代码
2013/09/27 PHP
windows8.1下Apache+Php+MySQL配置步骤
2015/10/30 PHP
PHP简单判断手机设备的方法
2016/08/23 PHP
php实现小程序支付完整版
2018/10/09 PHP
PHP+Ajax简单get验证操作示例
2019/03/02 PHP
PHP中的输出echo、print、printf、sprintf、print_r和var_dump的示例代码
2020/12/01 PHP
使用JS操作页面表格,元素的一些技巧
2007/02/02 Javascript
网页中CDATA标记的说明
2010/09/12 Javascript
ASP.NET jQuery 实例11 通过使用jQuery validation插件简单实现用户登录页面验证功能
2012/02/03 Javascript
IE6已终止操作问题的2种情况及解决
2014/04/23 Javascript
jquery实现翻动fadeIn显示的方法
2015/03/05 Javascript
详细分析使用AngularJS编程中提交表单的方式
2015/06/19 Javascript
JS实现仿FLASH效果的竖排导航代码
2015/09/15 Javascript
angularjs学习笔记之双向数据绑定
2015/09/26 Javascript
基于javascript制作经典传统的拼图游戏
2016/03/22 Javascript
JS封装的选项卡TAB切换效果示例
2016/09/20 Javascript
微信小程序 免费SSL证书https、TLS版本问题的解决办法
2016/12/14 Javascript
JSON键值对序列化和反序列化解析
2017/01/24 Javascript
详解keep-alive + vuex 让缓存的页面灵活起来
2019/04/19 Javascript
解决使用layui对select append元素无效或者未及时更新的问题
2019/09/18 Javascript
JavaScript中window和document用法详解
2020/07/28 Javascript
vue中实现弹出层动画效果的示例代码
2020/09/25 Javascript
python错误:AttributeError: 'module' object has no attribute 'setdefaultencoding'问题的解决方法
2014/08/22 Python
不同版本中Python matplotlib.pyplot.draw()界面绘制异常问题的解决
2017/09/24 Python
Pycharm无法使用已经安装Selenium的解决方法
2018/10/13 Python
python实现汽车管理系统
2018/11/30 Python
使用Pyinstaller转换.py文件为.exe可执行程序过程详解
2019/08/06 Python
解决tensorflow 释放图,删除变量问题
2020/06/23 Python
通过代码实例解析Pytest运行流程
2020/08/20 Python
Python爬虫使用bs4方法实现数据解析
2020/08/25 Python
python+requests接口自动化框架的实现
2020/08/31 Python
python里反向传播算法详解
2020/11/22 Python
Wiggle新西兰:自行车、跑步、游泳
2020/05/06 全球购物
大学活动策划书范文
2014/01/10 职场文书
补充协议书范本
2014/04/23 职场文书
计划生育证明书写要求
2014/09/17 职场文书