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解决字典中的值是列表问题的方法
Mar 04 Python
Python2中的raw_input() 与 input()
Jun 12 Python
Python在groupby分组后提取指定位置记录方法
Apr 20 Python
对python sklearn one-hot编码详解
Jul 10 Python
详解Python3中ceil()函数用法
Feb 19 Python
Python常用爬虫代码总结方便查询
Feb 25 Python
python3实现小球转动抽奖小游戏
Apr 15 Python
python 利用已有Ner模型进行数据清洗合并代码
Dec 24 Python
tensorflow之自定义神经网络层实例
Feb 07 Python
一篇文章搞懂python的转义字符及用法
Sep 03 Python
matplotlib部件之套索Lasso的使用
Feb 24 Python
Python使用openpyxl批量处理数据
Jun 23 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写的加密函数,支持私人密钥(详细介绍)
2013/06/09 PHP
PHP_NETWORK_GETADDRESSES: GETADDRINFO FAILED问题解决办法
2014/05/04 PHP
实例分析基于PHP微信网页获取用户信息
2017/11/24 PHP
php微信开发之关键词回复功能
2018/06/13 PHP
服务器端的JavaScript脚本 Node.js 使用入门
2012/03/07 Javascript
javascript判断非数字的简单例子
2013/07/18 Javascript
jQuery避免$符和其他JS库冲突的方法对比
2014/02/20 Javascript
jQuery制作的别致导航有阴影背景高亮模式窗口
2014/04/15 Javascript
jQuery封装的tab选项卡插件分享
2015/06/16 Javascript
学习JavaScript设计模式(代理模式)
2015/12/03 Javascript
Google 地图叠加层实例讲解
2016/08/06 Javascript
jquery实现全选、不选、反选的两种方法
2016/09/06 Javascript
解决JS外部文件中文注释出现乱码问题
2017/07/09 Javascript
vue项目实现记住密码到cookie功能示例(附源码)
2018/01/31 Javascript
基于Angular 8和Bootstrap 4实现动态主题切换的示例代码
2020/02/11 Javascript
vue element el-transfer增加拖拽功能
2021/01/15 Vue.js
[03:17]2016完美“圣”典风云人物:冷冷专访
2016/12/08 DOTA
[01:05:59]Mineski vs Secret 2019国际邀请赛淘汰赛 败者组 BO3 第二场 8.22
2019/09/05 DOTA
Python使用xlrd模块操作Excel数据导入的方法
2015/05/26 Python
用Python的Flask框架结合MySQL写一个内存监控程序
2015/11/07 Python
Django objects的查询结果转化为json的三种方式的方法
2018/11/07 Python
Python算法中的时间复杂度问题
2019/11/19 Python
Django自带的加密算法及加密模块详解
2019/12/03 Python
jupyter notebook远程访问不了的问题解决方法
2021/01/11 Python
Python爬取梨视频的示例
2021/01/29 Python
CSS3实现可关闭的下拉手风琴菜单效果
2015/08/31 HTML / CSS
Sneaker Studio捷克:购买运动鞋
2018/07/08 全球购物
纽约州一群才华横溢的金匠制作而成:Hearth Jewelry
2019/03/22 全球购物
经管应届生求职信
2013/11/17 职场文书
学习交流会主持词
2014/04/01 职场文书
社区党员公开承诺书
2014/08/30 职场文书
竞选大队干部演讲稿
2014/09/11 职场文书
2014年教研组工作总结
2014/11/26 职场文书
关于清明节的演讲稿2015
2015/03/18 职场文书
升学宴学生致辞
2015/09/29 职场文书
2016年保险公众宣传日活动总结
2016/04/05 职场文书