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 相关文章推荐
tensorflow训练中出现nan问题的解决
Feb 10 Python
python如何通过twisted实现数据库异步插入
Mar 20 Python
DataFrame 将某列数据转为数组的方法
Apr 13 Python
快速解决PyCharm无法引用matplotlib的问题
May 24 Python
python3使用SMTP发送HTML格式邮件
Jun 19 Python
Python wxPython库消息对话框MessageDialog用法示例
Sep 03 Python
Python中的集合介绍
Jan 28 Python
python 获得任意路径下的文件及其根目录的方法
Feb 16 Python
python爬虫 猫眼电影和电影天堂数据csv和mysql存储过程解析
Sep 05 Python
wxpython绘制音频效果
Nov 18 Python
Python守护进程实现过程详解
Feb 10 Python
python实现Pyecharts实现动态地图(Map、Geo)
Mar 25 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
苏联队长,苏联超人蝙蝠侠,这些登场的“山寨”英雄真的很严肃
2020/04/09 欧美动漫
php图片验证码代码
2008/03/27 PHP
对淘宝URL中ID提取的PHP代码
2013/09/01 PHP
PHP7 echo和print语句实例用法
2019/02/15 PHP
PHP+MySQL实现在线测试答题实例
2020/01/02 PHP
js的正则test,match,exec详细解析
2014/01/29 Javascript
jquery操作checked属性以及disabled属性的多种方法
2014/06/20 Javascript
js实现拉幕效果的广告代码
2015/09/02 Javascript
js实现不重复导入的方法
2016/03/02 Javascript
Bootstrap 附加导航(Affix)插件实例详解
2016/06/01 Javascript
什么是JavaScript注入攻击?
2016/09/14 Javascript
微信小程序 首页制作简单实例
2017/04/07 Javascript
jQuery实现动态添加、删除按钮及input输入框的方法
2017/04/27 jQuery
vue.js系列中的vue-fontawesome使用
2018/02/10 Javascript
Javascript Promise用法详解
2018/05/10 Javascript
JS动态插入脚本和插入引用外部链接脚本的方法
2018/05/21 Javascript
JS对象与json字符串相互转换实现方法示例
2018/06/14 Javascript
Javascript格式化并高亮xml字符串的方法及注意事项
2018/08/13 Javascript
Vue封装的可编辑表格插件方法
2018/08/28 Javascript
js核心基础之闭包的应用实例分析
2019/05/11 Javascript
WebStorm中如何将自己的代码上传到github示例详解
2020/10/28 Javascript
python 双循环遍历list 变量判断代码
2020/05/04 Python
深入理解Python 多线程
2020/06/16 Python
Html5 FileReader实现即时上传图片功能实例代码
2014/09/01 HTML / CSS
美国最大的珠宝首饰网上商城:Jewelry.com
2016/07/22 全球购物
CAT鞋英国官网:坚固耐用的靴子和鞋
2016/10/21 全球购物
海蓝之谜英国官网:La Mer英国
2020/01/15 全球购物
爱尔兰旅游网站:ebookers.ie
2020/01/24 全球购物
教师个人的自我评价分享
2014/01/02 职场文书
农村婚礼主持词
2014/03/13 职场文书
大班幼儿评语大全
2014/04/30 职场文书
幼儿园园长新年寄语2015
2014/12/08 职场文书
2014年车间主任工作总结
2014/12/10 职场文书
大学生活委员竞选稿
2015/11/21 职场文书
《这片土地是神圣的》教学反思
2016/02/16 职场文书
辞职信怎么写?
2019/05/21 职场文书