Python实现异步IO的示例
作者:-零 发布时间:2021-01-11 13:25:38
前言
用阻塞 API 写同步代码最简单,但一个线程同一时间只能处理一个请求,有限的线程数导致无法实现万级别的并发连接,过多的线程切换也抢走了 CPU 的时间,从而降低了每秒能够处理的请求数量。为了达到高并发,你可能会选择一个异步框架,用非阻塞 API 把业务逻辑打乱到多个回调函数,通过多路复用与事件循环的方式实现高并发。
磁盘 IO 为例,描述了多线程中使用阻塞方法读磁盘,2 个线程间的切换方式。那么,怎么才能实现高并发呢?
把上图中本来由内核实现的请求切换工作,交由用户态的代码来完成就可以了,异步化编程通过应用层代码实现了请求切换,降低了切换成本和内存占用空间。异步化依赖于 IO 多路复用机制,比如 Linux 的 epoll 或者 Windows 上的 iocp,同时,必须把阻塞方法更改为非阻塞方法,才能避免内核切换带来的巨大消耗。Nginx、Redis 等高性能服务都依赖异步化实现了百万量级的并发。
下图描述了异步 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
来源:https://www.cnblogs.com/-wenli/p/13819576.html
猜你喜欢
- 前言无聊的时候做了一个搜索文章的软件,有没有更加的方便快捷不知道,好玩就行了环境使用Python 3.8Pycharm模块使用import
- 目录函数什么是函数/方法2.为什么需要函数1、载体2、组织3、复用4、封装5、清晰6、按需3.如何声明/调用一个函数4.函数/方法的参数1、
- 在windows下安装Mysql系统日志出现max_open_files: 2048 max_connections: 510 table_
- 一、先让飞机在屏幕上飞起来吧。(一)实现飞机类class Plane: def __init__(self,fil
- 一.JavaScript基本介绍js诞生于1995年,是Javascript的缩写,其与java语言没有关系,当时的主要目的是验证表单的数据
- 一、下载1、官网下载2、某度网盘下载链接: https://pan.baidu.com/s/1BgbZH-aFaJ1nwm2PpDeOSQ?
- function is_utf8($string) { &n
- golang中允许对值为 nil 的 slice 添加元素package main func main() { var s []int s
- 本文实例为大家分享了python处理大日志文件的具体代码,供大家参考,具体内容如下# coding=utf-8import sysimpor
- 1. 云开发简介由于小程序本身存储数据的能力有限,所以不可能将大量的数据保存在客户端,而且将数据保存在本地既不安全,也无法与其他小程序用户共
- 实战场景这次被我们盯上的平台是【SMZDM】。本次目标站点是:aHR0cHM6Ly93d3cuc216ZG0uY29tLw==。正式开始前,
- 本文介绍了关于redux-saga中take使用方法详解,分享给大家,具体如下:带来一个自己研究好久的API使用方法.redux-saga中
- 一、下载软件1. 进入MySQL官网,登陆自己的Oracle账号(没有账号的自己注册一个),下载Mysql-5.7.17,下载地址:http
- 前言十三届全国人大三次会议作了政府工作报告。这份政府工作报告仅有10500字左右,据悉是改革开放40年以来最短的一次。受到疫情影响,今年的两
- 因此,在数据库的日常维护工作中,如果只是一次两次碰到ORA-01555错误,一般都先忽略,但是如果经常碰到该错误,则要进行一些调整以避免该错
- 有些时间没更新blog了,这两天为了更新<code collection>,于是重写了语法高亮的模块,这次是一个引擎,你可以根据
- 为啥要写这个脚本五一前的准备下班的时候,看到同事为了做数据库的某个表的数据字典,在做一个复杂的人工操作,就是一个字段一个字段的纯手撸,那速度
- 本文实例为大家分享了基于Express框架使用POST传递Form数据的具体代码,供大家参考,具体内容如下客户端使用Form发送数据在客户端
- python 实现单例的方法第一种方法:使用基类New 是真正创建实例对象的方法,所以重写基类的new 方法,以此保证创建对象的时候只生成一
- 引言除非您正在对服务进行原型设计,否则您可能会关心应用程序的内存使用情况。内存占用更小,基础设施成本降低,扩展变得更容易/延迟。尽管 Go