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 相关文章推荐
python抓取某汽车网数据解析html存入excel示例
Dec 04 Python
Python yield 小结和实例
Apr 25 Python
Python学习笔记(一)(基础入门之环境搭建)
Jun 05 Python
Python实现高效求解素数代码实例
Jun 30 Python
Python 中开发pattern的string模板(template) 实例详解
Apr 01 Python
用十张图详解TensorFlow数据读取机制(附代码)
Feb 06 Python
在Python中使用Neo4j的方法
Mar 14 Python
基于Python2、Python3中reload()的不同用法介绍
Aug 12 Python
Python 3.8 新功能来一波(大部分人都不知道)
Mar 11 Python
PIL.Image.open和cv2.imread的比较与相互转换的方法
Jun 03 Python
Tensorflow之MNIST CNN实现并保存、加载模型
Jun 17 Python
python代码能做成软件吗
Jul 24 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函数
2006/10/09 PHP
配置支持SSI
2006/11/25 PHP
深入理解PHP之数组(遍历顺序)  Laruence原创
2012/06/13 PHP
mac下Apache + MySql + PHP搭建网站开发环境
2014/06/02 PHP
php获取字符串中各个字符出现次数的方法
2015/02/23 PHP
再谈IE中Flash控件的自动激活 ObjectWrap
2007/03/09 Javascript
javascript Zifa FormValid 0.1表单验证 代码打包下载
2007/06/08 Javascript
JavaScript 调试器简介
2009/02/21 Javascript
新鲜出炉的js tips提示效果
2011/04/03 Javascript
JavaScript中的View-Model使用介绍
2011/08/11 Javascript
js中 关于undefined和null的区别介绍
2013/04/16 Javascript
对frameset、frame、iframe的js操作示例代码
2013/08/16 Javascript
JavaScript学习笔记之JS事件对象
2015/01/22 Javascript
jQuery+PHP实现微信转盘抽奖功能的方法
2016/05/25 Javascript
jQuery实现的表头固定效果实例【附完整demo源码下载】
2016/08/01 Javascript
AngularJS 2.0入门权威指南
2016/10/08 Javascript
解析JavaScript数组方法reduce
2016/12/12 Javascript
JavaScript中Require调用js的实例分享
2017/10/27 Javascript
javascript数据结构之多叉树经典操作示例【创建、添加、遍历、移除等】
2018/08/01 Javascript
详解项目升级到vue-cli3的正确姿势
2019/01/28 Javascript
vue中使用rem布局代码详解
2019/10/30 Javascript
[04:49]2014DOTA2国际邀请赛 Newbee顺利挺进总决赛 ImbaTV独家专访
2014/07/19 DOTA
从零学Python之入门(二)基本数据类型
2014/05/25 Python
对Python中DataFrame按照行遍历的方法
2018/04/08 Python
python装饰器简介---这一篇也许就够了(推荐)
2019/04/01 Python
Python标准库shutil模块使用方法解析
2020/03/10 Python
Python unittest discover批量执行代码实例
2020/09/08 Python
发现两个有趣的CSS3动画效果
2013/08/14 HTML / CSS
详解通过focusout事件解决IOS键盘收起时界面不归位的问题
2019/07/18 HTML / CSS
eDreams巴西:廉价机票,酒店优惠和度假套餐
2017/04/14 全球购物
BookOutlet加拿大:在网上书店购买廉价折扣图书和小说
2018/10/05 全球购物
计算机软件个人的自荐信范文
2013/12/01 职场文书
个人自我评价和职业目标
2014/01/24 职场文书
金正昆讲礼仪观后感
2015/06/11 职场文书
一看就懂的MySQL的聚簇索引及聚簇索引是如何长高的
2021/05/25 MySQL
Centos7 Shell编程之正则表达式、文本处理工具详解
2022/08/05 Servers