前言
用阻塞 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
以上就是Python实现异步IO的示例的详细内容,更多关于python 异步IO的资料请关注其它相关文章!
《魔兽世界》大逃杀!60人新游玩模式《强袭风暴》3月21日上线
暴雪近日发布了《魔兽世界》10.2.6 更新内容,新游玩模式《强袭风暴》即将于3月21 日在亚服上线,届时玩家将前往阿拉希高地展开一场 60 人大逃杀对战。
艾泽拉斯的冒险者已经征服了艾泽拉斯的大地及遥远的彼岸。他们在对抗世界上最致命的敌人时展现出过人的手腕,并且成功阻止终结宇宙等级的威胁。当他们在为即将于《魔兽世界》资料片《地心之战》中来袭的萨拉塔斯势力做战斗准备时,他们还需要在熟悉的阿拉希高地面对一个全新的敌人──那就是彼此。在《巨龙崛起》10.2.6 更新的《强袭风暴》中,玩家将会进入一个全新的海盗主题大逃杀式限时活动,其中包含极高的风险和史诗级的奖励。
《强袭风暴》不是普通的战场,作为一个独立于主游戏之外的活动,玩家可以用大逃杀的风格来体验《魔兽世界》,不分职业、不分装备(除了你在赛局中捡到的),光是技巧和战略的强弱之分就能决定出谁才是能坚持到最后的赢家。本次活动将会开放单人和双人模式,玩家在加入海盗主题的预赛大厅区域前,可以从强袭风暴角色画面新增好友。游玩游戏将可以累计名望轨迹,《巨龙崛起》和《魔兽世界:巫妖王之怒 经典版》的玩家都可以获得奖励。
更新日志
- 罗志祥《舞状元 (Explicit)》[320K/MP3][66.77MB]
- 尤雅.1997-幽雅精粹2CD【南方】【WAV+CUE】
- 张惠妹.2007-STAR(引进版)【EMI百代】【WAV+CUE】
- 群星.2008-LOVE情歌集VOL.8【正东】【WAV+CUE】
- 罗志祥《舞状元 (Explicit)》[FLAC/分轨][360.76MB]
- Tank《我不伟大,至少我能改变我。》[320K/MP3][160.41MB]
- Tank《我不伟大,至少我能改变我。》[FLAC/分轨][236.89MB]
- CD圣经推荐-夏韶声《谙2》SACD-ISO
- 钟镇涛-《百分百钟镇涛》首批限量版SACD-ISO
- 群星《继续微笑致敬许冠杰》[低速原抓WAV+CUE]
- 潘秀琼.2003-国语难忘金曲珍藏集【皇星全音】【WAV+CUE】
- 林东松.1997-2039玫瑰事件【宝丽金】【WAV+CUE】
- 谭咏麟.2022-倾·听【环球】【WAV+CUE】
- 4complete《丛生》[320K/MP3][85.26MB]
- 4complete《丛生》[FLAC/分轨][218.01MB]