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爬取Coursera课程资源的详细过程
Nov 04 Python
用实例分析Python中method的参数传递过程
Apr 02 Python
Python三种遍历文件目录的方法实例代码
Jan 19 Python
利用Python求阴影部分的面积实例代码
Dec 05 Python
python 实现得到当前时间偏移day天后的日期方法
Dec 31 Python
python 处理数字,把大于上限的数字置零实现方法
Jan 28 Python
Python3实现的简单三级菜单功能示例
Mar 12 Python
利用Python半自动化生成Nessus报告的方法
Mar 19 Python
Python3.7 新特性之dataclass装饰器
May 27 Python
python networkx 根据图的权重画图实现
Jul 10 Python
使用tensorflow实现矩阵分解方式
Feb 07 Python
这样写python注释让代码更加的优雅
Jun 02 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 自写函数代码 获取关键字 去超链接
2010/02/08 PHP
ajax php传递和接收变量实现思路及代码
2012/12/19 PHP
zend optimizer在wamp的基础上安装图文教程
2013/10/26 PHP
Javascript 继承机制实例
2009/08/12 Javascript
js中巧用cssText属性批量操作样式
2011/03/13 Javascript
基于jquery的textarea发布框限制文字字数输入(添加中文识别)
2012/02/16 Javascript
JavaScript避免代码的重复执行经验技巧分享
2014/04/17 Javascript
JavaScript实现弹出子窗口并传值给父窗口
2014/12/18 Javascript
使用iojs的jsdom库实现同步系统时间
2015/04/20 Javascript
jquery设置表单元素为不可用的简单代码
2016/07/04 Javascript
html判断当前页面是否在iframe中的实例
2016/11/30 Javascript
Node.js 的模块知识汇总
2017/08/16 Javascript
Vue-resource拦截器判断token失效跳转的实例
2017/10/27 Javascript
微信小程序wx.request拦截器使用详解
2019/07/09 Javascript
python实现dict版图遍历示例
2014/02/19 Python
Python实现八大排序算法
2016/08/13 Python
听歌识曲--用python实现一个音乐检索器的功能
2016/11/15 Python
解决Python selenium get页面很慢时的问题
2019/01/30 Python
在pyqt5中QLineEdit里面的内容回车发送的实例
2019/06/21 Python
解决python多行注释引发缩进错误的问题
2019/08/23 Python
python脚本实现mp4中的音频提取并保存在原目录
2020/02/27 Python
不到20行实现Python代码即可制作精美证件照
2020/04/24 Python
Python使用sqlite3模块内置数据库
2020/05/07 Python
详解Python多线程下的list
2020/07/03 Python
python实现数学模型(插值、拟合和微分方程)
2020/11/13 Python
CSS3地图动态实例代码(圆圈向外扩散)
2018/06/15 HTML / CSS
世嘉游戏英国官方商店:SEGA Shop UK
2019/09/20 全球购物
化工专业推荐信范文
2013/11/28 职场文书
医疗纠纷协议书
2014/04/16 职场文书
《美丽的南沙群岛》教学反思
2014/04/27 职场文书
股权转让协议书
2014/12/07 职场文书
青岛海底世界导游词
2015/02/11 职场文书
2015年感恩母亲节活动方案
2015/05/04 职场文书
使用pytorch实现线性回归
2021/04/11 Python
pytorch--之halfTensor的使用详解
2021/05/24 Python
详解Redis瘦身指南
2021/05/26 Redis