首页 » SEO优化 » phpyield非壅塞技巧_Python实现线程的高效非壅塞IO调用

phpyield非壅塞技巧_Python实现线程的高效非壅塞IO调用

访客 2024-12-12 0

扫一扫用手机浏览

文章目录 [+]

import asyncioasync def do_some_work(x): print('Waiting: ', x) # 协程碰着await,事宜循环将会挂起该协程,实行别的协程,直到其他的协程也挂起或者实行完毕,再进行下一个协程的实行。
# 此处先挂起,再实行await的协程,末了实行return await asyncio.sleep(x) return 'Done after {}s'.format(x) coroutine = do_some_work(2)loop = asyncio.get_event_loop()loop.run_until_complete(coroutine)

上面的例子中,实现了协程,如果有多个coroutine同时实行do_some_work函数,可以达到非壅塞的效果,而不必壅塞等待每一个sleep完成

但是在一样平常python开拓中,我们会碰着很多的I/O调用,如网络I/O要求、数据库I/O要求等,这些调用实行韶光都比较长,如果有很多要求都要进行I/O操作,那么Python的单个进程模型里面,I/O会壅塞很多韶光,导致后面的要求相应韶光过长乃至超时,以是我们须要将更多的耗时I/O操作都采取非壅塞的办法,才能大大提高系统的实行效率和性能。

phpyield非壅塞技巧_Python实现线程的高效非壅塞IO调用

Python中一样平常的I/O调用方法

phpyield非壅塞技巧_Python实现线程的高效非壅塞IO调用
(图片来自网络侵删)

一样平常情形下,我们Python在实现I/O调用时,例如http要求,都会用requests库或者自带的urllib库来实现,方法如下:

from urllib import parse,requestimport json# POST要求 当request中包含data参数的时候,是POST要求,反之是GET要求textmod = {"username": "admin", "password": "123456"}textmod = json.dumps(textmod).encode(encoding='utf-8')header_dict = {'Accept': 'application/json', 'Content-Type': 'application/json'}url = 'http://localhost:8080/api/xxx'req = request.Request(url=url, data=textmod, headers=header_dict)res = request.urlopen(req)res = res.read()

上面这种一样平常的I/O调用方法便是壅塞式的,常用的还是连接数据库的SQLAlchemy、Django的orm等等。
如果采取这种办法支配在我们的api做事器,如flask、django等,就会在一个线程内造成壅塞,必须要采取多进程如gevent库、多线程模型等才能扛得起并发。

下面我们根据常用的I/O调用库来先容一些实现了aio(fei壅塞)的I/O调用库。

常用的异步I/O库

如果我们用协程实现一个sleep:

import asyncioimport timeasync def do_some_work(x): print('Waiting: ', x) time.sleep(x) return 'Done after {}s'.format(x) coroutine = do_some_work(2)loop = asyncio.get_event_loop()loop.run_until_complete(coroutine)

这种办法虽然定义了async方法,但是这是无法实现非壅塞的,程序运行到time.sleep时还是会壅塞x秒,无法利用await实现非壅塞,以是我们必须把time.sleep改为await asyncio.sleep(x)才能达到非壅塞的效果。

同样的,像urllib、requests、SQLAlchemy等都库的实行,都是壅塞式的,一个线程里面运行到对应的I/O调用方法时总会等待实行返回,而办理这种办法的路子只有多线程和协程,多线程在Python里面由于GIL锁的存在,效率有限而且不好管理,以是还是推举采取协程的方法办理。
这里先容一下几个实现了aio的http、mysql库,来源于第三方开拓者,详细的实现方法可以在github上查看。

aiohttp库

import asynciofrom aiohttp import ClientSessionurl = "https://www.baidu.com/{}"async def hello(url): async with ClientSession() as session: async with session.get(url) as response: response = await response.read() print(response)if __name__ == '__main__': coroutine = hello(url) loop = asyncio.get_event_loop() loop.run_until_complete(coroutine)

上面的例子就可以实现非壅塞的http要求,当多个coroutine任务一起放入loop.run_until_complete()方法实行时,coroutine1运行到await response.read()的时候,进入等待,但是同时coroutine2可以开始实行而不须要等待coroutine1,从而实现了非壅塞。

同样的办法还有aiomysql、aioredis等等

aiomysql库

import asyncioimport aiomysqlasync def test_example(loop): pool = await aiomysql.create_pool(host='127.0.0.1', port=3306, user='root', password='', db='mysql', loop=loop) async with pool.acquire() as conn: async with conn.cursor() as cur: await cur.execute("SELECT 42;") print(cur.description) (r,) = await cur.fetchone() assert r == 42 pool.close() await pool.wait_closed()loop = asyncio.get_event_loop()loop.run_until_complete(test_example(loop))

采取aiomysql库,创建数据库表、实行CRUD操作时都可以非壅塞,毕竟mysql本身是支持多线程的。

关于async和await

python里面async和await到底是怎么实现的呢?

其实在旧版本的python里面是采取yield关键字,将方法封装成一个天生器,让每个要实现协程的方法通过装饰器函数@coroutine的办法来封装的。
async和await是3.5版本之后的新语法,但是实现办法大同小异。

例如上面实现的asyncio.sleep()方法,在coroutine1、coroutine2等多个任务都传入loop.run_until_complete()时,程序实行到coroutine1的sleep会作为天生器变量保存起来,然后挂起coroutine1,实行coroutine2,而全体loop.run_until_complete()办法会遍历天生器的值,末了得到每个coroutine的结果再分别实行后面步骤,从而实现了非壅塞的效果。

但是该办法还是无法利用到多核CPU,以是现在做事器的最佳支配办法还是多进程+协程的办法

结合Tornado框架实现异步做事器

说完了python以脚本办法直接运行的协程实现方法,我们要来看看如果是做事器该如何实现协程办法运作。

python常用的flask、Django等框架,在运行api做事,监听端口时,是无法实现非壅塞的,以是flask和Django的支配办法常常采取了多线程来提高并发效率。

近几年涌现的Tornado框架是采取了I/O多路复用的epoll机制来实现的,这是称作一种uvloop。
底层方面,从一开始的loop封装逐步发展为基于python3.4之后涌现的asyncio库来封装,以是Tornado框架是由事宜驱动来作为api做事的。

基本的实现办法

import tornado.webimport tornado.httpserverimport tornado.ioloopclass IndexPageHandler(tornado.web.RequestHandler): def get(self): self.render('tornado_index.html')class Application(tornado.web.Application): def __init__(self): handlers = [ (r'/', IndexPageHandler), ] settings = {"template_path": "templates/"} tornado.web.Application.__init__(self, handlers, settings)if __name__ == '__main__': app = Application() server = tornado.httpserver.HTTPServer(app) server.listen(5000) tornado.ioloop.IOLoop.instance().start()

上面tornado框架的启动办法,是采取之前的ioloop办法的,这种办法如果在我们的掌握器IndexPageHandler里面定义了async get方法,则里面的await 是不能实现非壅塞的。
以是我们要采取为以下的asyncio+uvloop的事宜循环机制才能实现非壅塞:

非壅塞的实现办法

import tornado.webimport tornado.httpserverimport tornado.ioloopimport tornado.platform.asyncio as tornado_asyncioimport asyncioimport uvloopclass IndexPageHandler(tornado.web.RequestHandler): def get(self): self.render('tornado_index.html')class Application(tornado.web.Application): def __init__(self): handlers = [ (r'/', IndexPageHandler), ] settings = {"template_path": "templates/"} tornado.web.Application.__init__(self, handlers, settings)if __name__ == '__main__': asyncio.set_event_loop_policy(uvloop.EventLoopPolicy()) tornado_asyncio.AsyncIOMainLoop().install() app = Application() server = tornado.httpserver.HTTPServer(app) server.listen(5000) asyncio.get_event_loop().run_forever()

采取这种办法启动Tornado,就可以在我们的IndexPageHandler等多个Handler里面定义async方法,然后引入我们的aio库,用await办法韶光非壅塞了。

Tornado支配最佳实践

采取非壅塞办法驱动Tornado框架;

采取多进程办法运行api做事;

如果须要进行环境隔离或者快速扩容等支配,建议采取Docker办法实现

参考文献:

https://blog.csdn.net/brucewong0516/article/details/82697935

https://www.jianshu.com/p/b5e347b3a17c

https://juejin.im/post/6844903832716247053

末了多说一句,小编是一名python开拓工程师,这里有我自己整理了一套最新的python系统学习教程,包括从根本的python脚本到web开拓、爬虫、数据剖析、数据可视化、机器学习等。
想要这些资料的可以关注小编,并在后台私信小编:“01”即可领取。

标签:

相关文章