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 12 Python
Python栈算法的实现与简单应用示例
Nov 01 Python
Python多进程并发与多线程并发编程实例总结
Feb 08 Python
python学习基础之循环import及import过程
Apr 22 Python
python  Django中的apps.py的目的是什么
Oct 15 Python
在PyCharm中实现关闭一个死循环程序的方法
Nov 29 Python
python爬取百度贴吧前1000页内容(requests库面向对象思想实现)
Aug 10 Python
TensorFlow通过文件名/文件夹名获取标签,并加入队列的实现
Feb 17 Python
python统计字符串中字母出现次数代码实例
Mar 02 Python
Spring @Enable模块驱动原理及使用实例
Jun 23 Python
opencv实现图像平移效果
Mar 24 Python
python实现手机推送 代码也就10行左右
Apr 12 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 将bmp图片转为jpg等其他任意格式的图片
2009/06/21 PHP
PHP 冒泡排序算法的实现代码
2010/08/08 PHP
PHP操作数组的一些函数整理介绍
2011/07/17 PHP
php中使用DOM类读取XML文件的实现代码
2011/12/14 PHP
Erlang的运算符(比较运算符,数值运算符,移位运算符,逻辑运算符)
2012/07/23 PHP
php set_time_limit()函数的使用详解
2013/06/05 PHP
新浪SAE搭建PHP项目教程
2015/01/28 PHP
JQuery动态创建DOM、表单元素的实现代码
2011/08/09 Javascript
JS定时器实例详细分析
2013/10/11 Javascript
JavaScript面向对象的实现方法小结
2015/04/14 Javascript
jQuery代码实现发展历程时间轴特效
2015/07/30 Javascript
基于touch.js手势库+zepto.js插件开发图片查看器(滑动、缩放、双击缩放)
2016/11/17 Javascript
js实现前端分页页码管理
2017/01/06 Javascript
js实现下拉菜单效果
2017/03/01 Javascript
vue简单封装axios插件和接口的统一管理操作示例
2020/02/02 Javascript
微信小程序自定义支持图片的弹窗
2020/12/21 Javascript
nodejs中使用worker_threads来创建新的线程的方法
2021/01/22 NodeJs
python获取指定目录下所有文件名列表的方法
2015/05/20 Python
Python实现通过文件路径获取文件hash值的方法
2017/04/29 Python
Python中int()函数的用法浅析
2017/10/17 Python
Python实现希尔排序算法的原理与用法实例分析
2017/11/23 Python
Python编程实现的简单神经网络算法示例
2018/01/26 Python
python3爬虫获取html内容及各属性值的方法
2018/12/17 Python
浅谈Python采集网页时正则表达式匹配换行符的问题
2018/12/20 Python
python的内存管理和垃圾回收机制详解
2019/05/18 Python
如何将 awk 脚本移植到 Python
2019/12/09 Python
使用Pandas将inf, nan转化成特定的值
2019/12/19 Python
Selenium及python实现滚动操作多种方法
2020/07/21 Python
利用CSS3实现的文字定时向上滚动
2016/08/29 HTML / CSS
美国礼品卡交易网站:Cardpool
2018/08/27 全球购物
杭州-飞时达软件有限公司.net笔面试
2012/04/28 面试题
实习生自荐信范文分享
2013/11/27 职场文书
开展党的群众路线教育实践活动方案
2014/02/05 职场文书
国际商贸专业自荐信
2014/06/09 职场文书
读《茶花女》有感:山茶花的盛开与凋零
2020/01/17 职场文书
GO中sync包自由控制并发示例详解
2022/08/05 Golang