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实现简单EXCEL数据统计的实例
Jan 24 Python
python连接数据库的方法
Oct 19 Python
python+matplotlib实现动态绘制图片实例代码(交互式绘图)
Jan 20 Python
Python数据集切分实例
Dec 08 Python
用Python获取摄像头并实时控制人脸的实现示例
Jul 11 Python
详解python实现小波变换的一个简单例子
Jul 18 Python
django将数组传递给前台模板的方法
Aug 06 Python
解决python多行注释引发缩进错误的问题
Aug 23 Python
Python在OpenCV里实现极坐标变换功能
Sep 02 Python
Python使用matplotlib绘制三维参数曲线操作示例
Sep 10 Python
安装2019Pycharm最新版本的教程详解
Oct 22 Python
使用Pandas的Series方法绘制图像教程
Dec 04 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删除HTMl标签的实现代码
2013/06/30 PHP
源码分析 Laravel 重复执行同一个队列任务的原因
2017/12/25 PHP
PHP htmlspecialchars()函数用法与实例讲解
2019/03/08 PHP
ie6下png图片背景不透明的解决办法使用js实现
2013/01/11 Javascript
对frameset、frame、iframe的js操作示例代码
2013/08/16 Javascript
javascript验证身份证完全方法具体实现
2013/11/18 Javascript
jQuery及JS实现循环中暂停的方法
2015/02/02 Javascript
元素绑定click点击事件方法
2015/06/08 Javascript
jQuery给div,Span, a ,button, radio 赋值与取值
2016/06/24 Javascript
vuejs在解析时出现闪烁的原因及防止闪烁的方法
2016/09/19 Javascript
AngularJS解决ng界面长表达式(ui-set)的方法分析
2016/11/07 Javascript
jQuery模拟完美实现经典FLASH导航动画效果【附demo源码下载】
2016/11/09 Javascript
Nuxt配合Node在实际生产中的应用详解
2018/08/07 Javascript
jquery分页插件pagination使用教程
2018/10/23 jQuery
JavaScript实现学生在线做题计时器功能
2018/12/05 Javascript
微信小程序实现通过js操作wxml的wxss属性示例
2018/12/06 Javascript
原生js实现表格循环滚动
2020/11/24 Javascript
连接Python程序与MySQL的教程
2015/04/29 Python
解析Python编程中的包结构
2015/10/25 Python
python使用logging模块发送邮件代码示例
2018/01/18 Python
python 实现对文件夹内的文件排序编号
2018/04/12 Python
pandas 空数据处理方法详解
2019/11/02 Python
Pytorch自定义Dataset和DataLoader去除不存在和空数据的操作
2021/03/03 Python
AmazeUI 等分网格的实现示例
2020/08/25 HTML / CSS
世界上最好的精品店:Shoptiques
2018/02/05 全球购物
澳大利亚珍珠首饰购物网站:Vayo Pearls
2019/03/11 全球购物
英国领先的维生素和补充剂品牌:Higher Nature
2019/08/26 全球购物
Diptyque英国官方网站:源自法国的知名香氛品牌
2019/08/28 全球购物
数据库方面面试题
2012/04/22 面试题
介绍一下javax.servlet.Servlet接口及其主要方法
2015/11/30 面试题
小学爱国卫生月活动总结
2014/06/30 职场文书
征用土地赔偿协议书
2014/09/26 职场文书
四风问题对照检查材料整改措施
2014/09/27 职场文书
个人工作违纪检讨书
2015/05/05 职场文书
晚会开幕词范文
2016/03/04 职场文书
在虚拟机中安装windows server 2008的图文教程
2022/06/28 Servers