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基础教程之python消息摘要算法使用示例
Feb 10 Python
web.py获取上传文件名的正确方法
Aug 26 Python
python检测是文件还是目录的方法
Jul 03 Python
利用Python自动监控网站并发送邮件告警的方法
Aug 24 Python
利用matplotlib+numpy绘制多种绘图的方法实例
May 03 Python
利用python为运维人员写一个监控脚本
Mar 25 Python
python实现对指定字符串补足固定长度倍数截断输出的方法
Nov 15 Python
详解如何用TensorFlow训练和识别/分类自定义图片
Aug 05 Python
Python Numpy 控制台完全输出ndarray的实现
Feb 19 Python
Python实现加密接口测试方法步骤详解
Jun 05 Python
Django利用AJAX技术实现博文实时搜索
May 06 Python
Pygame游戏开发之太空射击实战敌人精灵篇
Aug 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版(2)
2006/10/09 PHP
php浏览历史记录的方法
2015/03/10 PHP
php将数组转换成csv格式文件输出的方法
2015/03/14 PHP
ExtJS 设置级联菜单的默认值
2010/06/13 Javascript
Jquery刷新页面背景图片随机变换的实现方法
2013/03/15 Javascript
基于jquery插件实现常见的幻灯片效果
2013/11/01 Javascript
为jquery的ajaxfileupload增加附加参数的方法
2014/03/04 Javascript
常用的JS验证和函数汇总
2014/12/23 Javascript
jQuery ajax提交Form表单实例(附demo源码)
2016/04/06 Javascript
javacript获取当前屏幕大小
2016/06/04 Javascript
Vue.js路由vue-router使用方法详解
2017/03/20 Javascript
js中的DOM模拟购物车功能
2017/03/22 Javascript
bing Map 在vue项目中的使用详解
2018/04/09 Javascript
如何将百度地图包装成Vue的组件的方法步骤
2019/02/12 Javascript
微信小程序非跳转式组件授权登录的方法示例
2019/05/22 Javascript
Python列表(list)、字典(dict)、字符串(string)基本操作小结
2014/11/28 Python
Linux 发邮件磁盘空间监控(python)
2016/04/23 Python
Python获取暗黑破坏神3战网前1000命位玩家的英雄技能统计
2016/07/04 Python
Python cookbook(数据结构与算法)实现优先级队列的方法示例
2018/02/18 Python
如何基于pythonnet调用halcon脚本
2020/01/20 Python
Python实现括号匹配方法详解
2020/02/10 Python
python def 定义函数,调用函数方式
2020/06/02 Python
Django REST Framework 分页(Pagination)详解
2020/11/30 Python
python对输出的奇数偶数排序实例代码
2020/12/04 Python
Pytorch 图像变换函数集合小结
2021/02/01 Python
基于css3 animate制作绚丽的动画效果
2015/11/24 HTML / CSS
La Redoute英国官网:法国时尚品牌
2017/04/27 全球购物
恶搞卫生巾广告词
2014/03/18 职场文书
计生工作先进事迹
2014/08/15 职场文书
2014年学校卫生工作总结
2014/11/20 职场文书
2014保险公司个人工作总结
2014/12/09 职场文书
写给妈妈的感谢信
2015/01/22 职场文书
期中考试后的感想
2015/08/07 职场文书
用Python将库打包发布到pypi
2021/04/13 Python
简单谈谈Python面向对象的相关知识
2021/06/28 Python
MySQL中的隐藏列的具体查看
2021/09/04 MySQL