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基于socket实现网络广播的方法
Apr 29 Python
Python读写Json涉及到中文的处理方法
Sep 12 Python
Python批量更改文件名的实现方法
Oct 29 Python
Python切片索引用法示例
May 15 Python
python flask安装和命令详解
Apr 02 Python
Python+opencv 实现图片文字的分割的方法示例
Jul 04 Python
pygame实现贪吃蛇游戏(上)
Oct 29 Python
python将数据插入数据库的代码分享
Aug 16 Python
基于Python中Remove函数的用法讨论
Dec 11 Python
python解决OpenCV在读取显示图片的时候闪退的问题
Feb 23 Python
聊一聊python常用的编程模块
May 14 Python
Python序列化模块JSON与Pickle
Jun 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程序中的常见漏洞进行攻击
2006/10/09 PHP
php查看session内容的函数
2008/08/27 PHP
php 方便水印和缩略图的图形类
2009/05/21 PHP
iOS10推送通知开发教程
2016/09/19 PHP
PHP HTTP 认证实例详解
2016/11/03 PHP
如何优雅的使用 laravel 的 validator验证方法
2018/11/11 PHP
浅谈php使用curl模拟多线程发送请求
2019/03/08 PHP
js Object2String方便查看js对象内容
2014/11/24 Javascript
jQuery带时间的日期控件代码分享
2015/08/26 Javascript
javascript手风琴下拉菜单实现代码
2015/11/12 Javascript
Knockout自定义绑定创建方法
2015/12/26 Javascript
去除字符串左右两边的空格(实现代码)
2016/05/12 Javascript
js canvas仿支付宝芝麻信用分仪表盘
2016/11/16 Javascript
利用CSS、JavaScript及Ajax实现图片预加载的方法
2016/11/29 Javascript
jQuery仿写百度百科的目录树
2017/01/03 Javascript
jQuery简单实现的HTML页面文本框模糊匹配查询功能完整示例
2018/05/09 jQuery
小程序实现左滑删除功能
2018/10/30 Javascript
vue+element-ui实现表格编辑的三种实现方式
2018/10/31 Javascript
jQuery 实现扁平式小清新导航
2020/07/07 jQuery
[41:41]TFT vs Secret Supermajor小组赛C组 BO3 第一场 6.3
2018/06/04 DOTA
python 文本单词提取和词频统计的实例
2018/12/22 Python
python字符串替换re.sub()方法解析
2019/09/18 Python
python将数据插入数据库的代码分享
2020/08/16 Python
MONNIER Frères英国官网:源自巴黎女士奢侈品配饰电商平台
2018/12/06 全球购物
Yahoo-PHP面试题4
2012/05/05 面试题
《大自然的语言》教学反思
2014/04/08 职场文书
会计专业毕业生自荐书
2014/06/25 职场文书
敬业奉献模范事迹材料
2014/12/24 职场文书
总经理岗位职责
2015/02/04 职场文书
2015年技术员工作总结
2015/04/10 职场文书
小学班主任教育随笔
2015/08/15 职场文书
2019年新郎保证书3篇
2019/10/17 职场文书
导游词之广州陈家祠
2019/10/21 职场文书
Springboot如何使用logback实现多环境配置?
2021/06/16 Java/Android
python编程简单几行代码实现视频转换Gif示例
2021/10/05 Python
安装Ruby和 Rails的详细步骤
2022/04/19 Ruby