Python实现异步IO的示例


Posted in Python onNovember 05, 2020

前言

用阻塞 API 写同步代码最简单,但一个线程同一时间只能处理一个请求,有限的线程数导致无法实现万级别的并发连接,过多的线程切换也抢走了 CPU 的时间,从而降低了每秒能够处理的请求数量。为了达到高并发,你可能会选择一个异步框架,用非阻塞 API 把业务逻辑打乱到多个回调函数,通过多路复用与事件循环的方式实现高并发。

磁盘 IO 为例,描述了多线程中使用阻塞方法读磁盘,2 个线程间的切换方式。那么,怎么才能实现高并发呢?

Python实现异步IO的示例

把上图中本来由内核实现的请求切换工作,交由用户态的代码来完成就可以了,异步化编程通过应用层代码实现了请求切换,降低了切换成本和内存占用空间。异步化依赖于 IO 多路复用机制,比如 Linux 的 epoll 或者 Windows 上的 iocp,同时,必须把阻塞方法更改为非阻塞方法,才能避免内核切换带来的巨大消耗。Nginx、Redis 等高性能服务都依赖异步化实现了百万量级的并发。

下图描述了异步 IO 的非阻塞读和异步框架结合后,是如何切换请求的。

Python实现异步IO的示例

然而,写异步化代码很容易出错。因为所有阻塞函数,都需要通过非阻塞的系统调用拆分成两个函数。虽然这两个函数共同完成一个功能,但调用方式却不同。第一个函数由你显式调用,第二个函数则由多路复用机制调用。

这种方式违反了软件工程的内聚性原则,函数间同步数据也更复杂。特别是条件分支众多、涉及大量系统调用时,异步化的改造工作会非常困难。

Python如何实现异步调用

from flask import Flask
import time
app = Flask(__name__)


@app.route('/bar')
def bar():
  time.sleep(1)
  return '<h1>bar!</h1>'

@app.route('/foo')
def foo():
  time.sleep(1)
  return '<h1>foo!</h1>'
if __name__ == '__main__':
  app.run(host='127.0.0.1',port=5555,debug=True)

采用同步的方式调用

import requests
import time

starttime = time.time()
print(requests.get('http://127.0.0.1:5555/bar').content)
print(requests.get('http://127.0.0.1:5555/foo').content)
print("消耗时间: ",time.time() -starttime)

b'<h1>bar!</h1>'
b'<h1>foo!</h1>'
消耗时间:  2.015509605407715

采样异步的方式调用:

重点:

1.将阻塞io改为非阻塞io;

2.多路复用io监听内核事件,事件触发通过回调函数;

3.用户态代码采取事件循环的方式获取事件,执行事件的回调函数;

import selectors
import socket
import time
# from asynrequest import ParserHttp
class asynhttp:
  def __init__(self):
    self.selecter = selectors.DefaultSelector()

  def get(self,url,optiondict = None):
    global reqcount
    reqcount += 1
    s = socket.socket()
    s.setblocking(False)
    try:
      s.connect(('127.0.0.1',5555))
    except BlockingIOError:
      pass
    requset = 'GET %s HTTP/1.0\r\n\r\n' % url
    callback = lambda : self.send(s,requset)
    self.selecter.register(s.fileno(),selectors.EVENT_WRITE,callback)

  def send(self,s,requset):
    self.selecter.unregister(s.fileno())
    s.send(requset.encode())
    chunks = []
    callback = lambda: self.recv(s,chunks)
    self.selecter.register(s.fileno(),selectors.EVENT_READ,callback)

  def recv(self,s,chunks):
    self.selecter.unregister(s.fileno())
    chunk = s.recv(1024)
    if chunk:
      chunks.append(chunk)
      callback = lambda: self.recv(s,chunks)
      self.selecter.register(s.fileno(), selectors.EVENT_READ, callback)
    else:
      global reqcount
      reqcount -= 1
      request_first,request_headers,request_content,_ = ParserHttp.parser(b''.join(chunks))
      print("解析数据:",request_first,request_headers,request_content)
      print((b''.join(chunks)).decode())
      return (b''.join(chunks)).decode()

starttime = time.time()
reqcount = 0
asynhttper = asynhttp()
asynhttper.get('/bar')
asynhttper.get('/foo')
while reqcount:
  events = asynhttper.selecter.select()
  for event,mask in events:
    func = event.data
    func()
print("消耗时间:" ,time.time() - starttime)

HTTP/1.0 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 13
Server: Werkzeug/1.0.1 Python/3.7.7
Date: Thu, 15 Oct 2020 03:28:16 GMT

<h1>bar!</h1>
HTTP/1.0 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 13
Server: Werkzeug/1.0.1 Python/3.7.7
Date: Thu, 15 Oct 2020 03:28:16 GMT

<h1>foo!</h1>
消耗时间: 1.0127637386322021

以上就是Python实现异步IO的示例的详细内容,更多关于python 异步IO的资料请关注三水点靠木其它相关文章!

Python 相关文章推荐
Tensorflow简单验证码识别应用
May 25 Python
PyQt5每天必学之工具提示功能
Apr 19 Python
Python实现多条件筛选目标数据功能【测试可用】
Jun 13 Python
Python subprocess模块功能与常见用法实例详解
Jun 28 Python
python实现列表中最大最小值输出的示例
Jul 09 Python
python实现大量图片重命名
Mar 23 Python
python将unicode和str互相转化的实现
May 11 Python
scrapy框架携带cookie访问淘宝购物车功能的实现代码
Jul 07 Python
python 绘制场景热力图的示例
Sep 23 Python
Lombok插件安装(IDEA)及配置jar包使用详解
Nov 04 Python
Python pygame实现中国象棋单机版源码
Jun 20 Python
python playwrigh框架入门安装使用
Jul 23 Python
Python requests HTTP验证登录实现流程
Nov 05 #Python
Python包资源下载路径报404解决方案
Nov 05 #Python
如何一键升级Python所有包
Nov 05 #Python
python实现磁盘日志清理的示例
Nov 05 #Python
Python常用外部指令执行代码实例
Nov 05 #Python
Python Pandas数据分析工具用法实例
Nov 05 #Python
Python jieba结巴分词原理及用法解析
Nov 05 #Python
You might like
利用PHP实现与ASP Banner组件相似的类
2006/10/09 PHP
php 删除数组元素
2009/01/16 PHP
php Static关键字实用方法
2010/06/04 PHP
PHP实现Redis单据锁以及防止并发重复写入
2018/04/10 PHP
彻底搞懂JS无缝滚动代码
2007/01/03 Javascript
菜鸟javascript基础整理1
2010/12/06 Javascript
基于JQuery 选择器使用说明介绍
2013/04/18 Javascript
如何实现textarea里的不同文本显示不同颜色
2014/01/20 Javascript
js从Cookies里面取值的简单实现
2014/06/30 Javascript
Jquery遍历Json数据的方法
2015/04/20 Javascript
jquery实现的淡入淡出下拉菜单效果
2015/08/25 Javascript
Node.js服务器环境下使用Mock.js拦截AJAX请求的教程
2016/05/23 Javascript
自己封装的一个原生JS拖动方法(推荐)
2016/11/22 Javascript
jQuery实现两列等高并自适应高度
2016/12/22 Javascript
微信小程序获取微信运动步数的实例代码
2017/07/20 Javascript
无限循环轮播图之运动框架(原生JS实现)
2017/10/01 Javascript
基于vue中解决v-for使用报红并出现警告的问题
2018/03/03 Javascript
vue axios 表单提交上传图片的实例
2018/03/16 Javascript
nodejs中express入门和基础知识点学习
2018/09/13 NodeJs
vue实现绑定事件的方法实例代码详解
2019/06/20 Javascript
JS实现联想、自动补齐国家或地区名称的功能
2020/07/07 Javascript
Python yield 使用浅析
2015/05/28 Python
Python机器学习之决策树算法实例详解
2017/12/06 Python
Python求解任意闭区间的所有素数
2018/06/10 Python
浅析Python pandas模块输出每行中间省略号问题
2018/07/03 Python
pygame游戏之旅 添加游戏暂停功能
2018/11/21 Python
python算法与数据结构之冒泡排序实例详解
2019/06/22 Python
Python Opencv任意形状目标检测并绘制框图
2019/07/23 Python
浅谈Pytorch中的自动求导函数backward()所需参数的含义
2020/02/29 Python
python如何实现DES加密
2020/09/21 Python
民族团结演讲稿范文
2014/08/27 职场文书
党章培训心得体会
2014/09/04 职场文书
丧事答谢词
2015/01/05 职场文书
读《皮囊》有感:理解是对他人的最大的善举
2019/11/14 职场文书
浅谈Python数学建模之线性规划
2021/06/23 Python
Java的Object类的九种方法
2022/04/13 Java/Android