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 用户登录验证的小例子
Mar 06 Python
Python冲顶大会 快来答题!
Jan 17 Python
flask框架实现连接sqlite3数据库的方法分析
Jul 16 Python
使用python生成杨辉三角形的示例代码
Aug 29 Python
python自动发邮件总结及实例说明【推荐】
May 31 Python
Python笔记之工厂模式
Nov 20 Python
TensorFlow绘制loss/accuracy曲线的实例
Jan 21 Python
python代码实现TSNE降维数据可视化教程
Feb 28 Python
Django form表单与请求的生命周期步骤详解
Jun 07 Python
Python 高效编程技巧分享
Sep 10 Python
Python获取指定网段正在使用的IP
Dec 14 Python
python中对列表的删除和添加方法详解
Feb 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
Windows下Apache + PHP SESSION丢失的解决过程全纪录
2015/04/07 PHP
PHP实现对xml的增删改查操作案例分析
2017/05/19 PHP
ASP中进行HTML数据及JS数据编码函数
2009/11/11 Javascript
判断iframe是否加载完成的完美方法
2010/01/07 Javascript
15个款优秀的 jQuery 图片特效插件推荐
2011/11/21 Javascript
javascript学习(一)构建自己的JS库
2013/01/02 Javascript
js/jquery判断浏览器的方法小结
2014/09/02 Javascript
javaScript中Math()函数注意事项
2015/06/18 Javascript
js中 javascript:void(0) 用法详解
2015/08/11 Javascript
Jquery幻灯片特效代码分享--鼠标点击按钮时切换(1)
2015/08/15 Javascript
JavaScript中this的9种应用场景及三种复合应用场景
2015/09/12 Javascript
基于jquery插件实现拖拽删除图片功能
2020/08/27 Javascript
js创建对象几种方式的优缺点对比
2016/09/28 Javascript
vue+Java后端进行调试时解决跨域问题的方式
2017/10/19 Javascript
canvas轨迹回放功能实现
2017/12/20 Javascript
es6新特性之 class 基本用法解析
2018/05/05 Javascript
LayUI表格批量删除方法
2018/08/15 Javascript
vue中的watch监听数据变化及watch中各属性的详解
2018/09/11 Javascript
原生javascript中this几种常见用法总结
2020/02/24 Javascript
[00:50]2014DOTA2国际邀请赛 NEWBEE战队回顾
2014/08/01 DOTA
Python调用C语言的方法【基于ctypes模块】
2018/01/22 Python
tensorflow使用神经网络实现mnist分类
2018/09/08 Python
python-itchat 统计微信群、好友数量,及原始消息数据的实例
2019/02/21 Python
python使用Paramiko模块实现远程文件拷贝
2019/04/30 Python
使用Pycharm分段执行代码
2020/04/15 Python
OpenCV+python实现实时目标检测功能
2020/06/24 Python
详解python datetime模块
2020/08/17 Python
东方电视购物:东方CJ
2016/10/12 全球购物
办加油卡单位介绍信
2014/01/09 职场文书
学校法制宣传月活动总结
2014/07/03 职场文书
2015年元旦主持词开场白
2014/12/14 职场文书
青年岗位能手事迹材料
2014/12/23 职场文书
2015年高中班级工作总结
2015/07/21 职场文书
《一面五星红旗》教学反思
2016/02/23 职场文书
深入探讨opencv图像矫正算法实战
2021/05/21 Python
详解使用 CSS prefers-* 规范提升网站的可访问性与健壮性
2021/05/25 HTML / CSS