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多线程操作实例
Nov 21 Python
理解Python中的类与实例
Apr 27 Python
Python的Django框架下管理站点的基本方法
Jul 17 Python
Ubuntu 下 vim 搭建python 环境 配置
Jun 12 Python
Python面向对象之继承代码详解
Jan 29 Python
Python数据可视化教程之Matplotlib实现各种图表实例
Jan 13 Python
django query模块
Apr 20 Python
python实现给微信指定好友定时发送消息
Apr 29 Python
50行Python代码获取高考志愿信息的实现方法
Jul 23 Python
Python面向对象原理与基础语法详解
Jan 02 Python
解析python 类方法、对象方法、静态方法
Aug 15 Python
用python画城市轮播地图
May 28 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数组无限分级数据的层级化处理代码
2012/12/29 PHP
解析用PHP实现var_export的详细介绍
2013/06/20 PHP
php调用自己java程序的方法详解
2016/05/13 PHP
php版微信开发Token验证失败或请求URL超时问题的解决方法
2016/09/23 PHP
javascript Firefox与IE 替换节点的方法
2010/02/24 Javascript
javascript定义函数的方法
2010/12/06 Javascript
根据IP的地址,区分不同的地区,查看不同的网站页面的js代码
2013/02/26 Javascript
获取select元素被选中的文本内容的js代码
2014/01/29 Javascript
Js实现简单的小球运动特效
2016/02/18 Javascript
Javascript实现图片加载从模糊到清晰显示的方法
2016/06/21 Javascript
百度多文件异步上传控件webuploader基本用法解析
2016/11/07 Javascript
arctext.js实现文字平滑弯曲弧形效果的插件
2019/05/13 Javascript
Django模板继承 extend标签实例代码详解
2019/05/16 Javascript
Vue 一键清空表单的实现方法
2020/02/07 Javascript
微信小程序实现左滑删除效果
2020/11/18 Javascript
[31:55]完美世界DOTA2联赛循环赛 IO vs GXR BO2第一场 11.04
2020/11/05 DOTA
Python 实现两个服务器之间文件的上传方法
2019/02/13 Python
Django 重写用户模型的实现
2019/07/29 Python
python 实现单通道转3通道
2019/12/03 Python
keras Lambda自定义层实现数据的切片方式,Lambda传参数
2020/06/11 Python
浅谈Keras的Sequential与PyTorch的Sequential的区别
2020/06/17 Python
Python如何读写二进制数组数据
2020/08/01 Python
python模拟点击玩游戏的实例讲解
2020/11/26 Python
台湾最大银发乐活百货:乐龄网
2018/05/21 全球购物
世界各地的旅游、观光和活动:Isango!
2019/10/29 全球购物
小学家长评语大全
2014/04/16 职场文书
竞选学习委员演讲稿
2014/04/28 职场文书
死亡证明书样本说明
2014/10/18 职场文书
八达岭长城导游词
2015/01/30 职场文书
归元寺导游词
2015/02/06 职场文书
2015年网络管理员工作总结
2015/05/21 职场文书
2019年中,最受大众欢迎的6本新书
2019/08/07 职场文书
导游词之山东八仙过海景区
2019/11/11 职场文书
springboot @ConfigurationProperties和@PropertySource的区别
2021/06/11 Java/Android
Java由浅入深通关抽象类与接口(上篇)
2022/04/26 Java/Android
SQL Server中使用表变量和临时表
2022/05/20 SQL Server