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实现的金山快盘的签到程序
Jan 17 Python
Python中__call__用法实例
Aug 29 Python
Python中的__new__与__init__魔术方法理解笔记
Nov 08 Python
Python3 模块、包调用&amp;路径详解
Oct 25 Python
python如何使用unittest测试接口
Apr 04 Python
python 3.3 下载固定链接文件并保存的方法
Dec 18 Python
python3.x实现base64加密和解密
Mar 28 Python
处理python中多线程与多进程中的数据共享问题
Jul 28 Python
深入了解如何基于Python读写Kafka
Dec 31 Python
Django中Aggregation聚合的基本使用方法
Jul 09 Python
【超详细】八大排序算法的各项比较以及各自特点
Mar 31 Python
Python Django模型详解
Oct 05 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
thinkphp5框架调用其它控制器方法 实现自定义跳转界面功能示例
2019/07/03 PHP
Laravel如何实现自动加载类
2019/10/14 PHP
Javascript 函数对象的多重身份
2009/06/28 Javascript
node.js中的http.request方法使用说明
2014/12/14 Javascript
fastclick插件导致日期(input[type=&quot;date&quot;])控件无法被触发该如何解决
2015/11/09 Javascript
Vue 固定头 固定列 点击表头可排序的表格组件
2016/11/25 Javascript
js实现旋转的星空效果
2019/11/01 Javascript
[04:00]黄浦江畔,再会英雄——完美世界DOTA2 TI9应援视频
2019/07/31 DOTA
解决python报错MemoryError的问题
2018/06/26 Python
全面了解django的缓存机制及使用方法
2019/07/22 Python
Django中的静态文件管理过程解析
2019/08/01 Python
Python自动化完成tb喵币任务的操作方法
2019/10/30 Python
你可能不知道的Python 技巧小结
2020/01/29 Python
Python实现分数序列求和
2020/02/25 Python
tensorflow模型转ncnn的操作方式
2020/05/25 Python
Python Merge函数原理及用法解析
2020/09/16 Python
利用css3实现的简单的鼠标悬停按钮
2014/11/04 HTML / CSS
css3的transform造成z-index无效解决方案
2014/12/04 HTML / CSS
伦敦一卡通:The London Pass
2018/11/30 全球购物
美国最佳选择产品网站:Best Choice Products
2019/05/27 全球购物
Otiumberg官网:英国半精致珠宝品牌
2021/01/16 全球购物
法警的竞聘演讲稿
2014/01/02 职场文书
2014年应届大学生自我评价
2014/01/09 职场文书
关于是否需要写商业计划书
2014/02/07 职场文书
小学一年级评语大全
2014/04/22 职场文书
师范大学生求职信
2014/06/13 职场文书
亲子阅读的活动方案
2014/08/15 职场文书
公司感恩节活动策划书
2014/10/11 职场文书
开展党的群众路线教育实践活动工作总结
2014/11/05 职场文书
入党介绍人意见怎么写
2015/06/03 职场文书
迎新生晚会主持词
2015/06/30 职场文书
我爱我班主题班会
2015/08/13 职场文书
迎客户欢迎词三篇
2019/09/27 职场文书
详解JSON.parse和JSON.stringify用法
2022/02/18 Javascript
css3 选择器
2022/05/11 HTML / CSS