python中的异步实践与tornado应用

最近项目中由于在python3中使用tornado,之前也有用过,是在python2中,由于对于协程理解不是很透彻,只是套用官方文档中的写法,最近比较细致的看了下协程的用法,也将tornado在python3中异步的实践了一下。

异步基础

要理解协程,先要理解异步,要理解异步,先要理解同步,与同步相关的概念又有阻塞与非阻塞,下面一一做简单介绍。

阻塞

阻塞状态指程序未得到所需计算资源时被挂起的状态。程序在等待某个操作完成期间,自身无法继续干别的事情,则称该程序在该操作上是阻塞的。

常见的阻塞形式有:网络 I/O 阻塞、磁盘 I/O 阻塞、用户输入阻塞等。阻塞是无处不在的,包括 CPU 切换上下文时,所有的进程都无法真正干事情,它们也会被阻塞。如果是多核 CPU 则正在执行上下文切换操作的核不可被利用。

非阻塞

程序在等待某操作过程中,自身不被阻塞,可以继续运行干别的事情,则称该程序在该操作上是非阻塞的。非阻塞并不是在任何程序级别、任何情况下都可以存在的。

仅当程序封装的级别可以囊括独立的子程序单元时,它才可能存在非阻塞状态。

非阻塞的存在是因为阻塞存在,正因为某个操作阻塞导致的耗时与效率低下,我们才要把它变成非阻塞的。

同步

不同程序单元为了完成某个任务,在执行过程中需靠某种通信方式以协调一致,称这些程序单元是同步执行的。

例如购物系统中更新商品库存,需要用“行锁”作为通信信号,让不同的更新请求强制排队顺序执行,那更新库存的操作是同步的。 简言之,同步意味着有序。

异步

为完成某个任务,不同程序单元之间过程中无需通信协调,也能完成任务的方式,不相关的程序单元之间可以是异步的。

例如,爬虫下载网页。调度程序调用下载程序后,即可调度其他任务,而无需与该下载任务保持通信以协调行为。不同网页的下载、保存等操作都是无关的,也无需相互通知协调。这些异步操作的完成时刻并不确定。 简言之,异步意味着无序。

这个概念让我想起了上学时学过的一篇文章,讲统筹安排的,比如你现在要烧水,做饭,洗衣服三件事,如果同步的进行,先烧水,在水烧开的过程中你什么都不做就等着它烧开,然后水烧开以后你再接着做饭,饭做熟的过程中你也是什么都不干,就干等着,饭做熟后再去将洗衣服放入洗衣机中去洗,之后又是干等着。如果单做一件事的时间是烧水10分别,做饭30分钟,洗衣服20分钟,那么完成这三件事总共需要60分钟。

如果将这三件事异步的去进行,我先将水烧上,然后再将衣服放到洗衣机里,然后去做饭,这三件事同时进行,当水烧开的时候给我一个信号,这里就是水壶会响,我听到响声以后我会中止做饭这件事情去处理烧开的水,比如把它倒到保温瓶中,衣服洗完以后洗衣机也会给我一个信号,那么我就会将衣服拿出来晾晒。这样处理完三件事总共的时间就由三件事情中最长的时间决定,这里就是30分钟,其实异步的处理就是最大程度的发挥cpu的处理能力,让其在同一时间内做更多的事情。

上面的过程用代码来实现大概是这个样子:

执行结果如下:

yield 语法

以上是用了多线程的方式来达到异步的效果,但是并没有用到协程,协程在python2就有,现在来看看在python2中通过yield语法。以下方法是在python2.6中执行。

要了解 yield 语法,先要了解一个概念: Generator 『生成器』,关于generator的概念可以参考廖雪峰的教程,写的很好。

如果一个函数定义中包含 yield 关键字,那么这个函数就不再是一个普通函数,而是一个 generator

运行该脚本以后程序并没有任何输出,因为它有yield表达式,因此,我们通过next()语句让它执行。next()语句将恢复Generator执行,并直到下一个yield表达式处。比如:

调用 c.nect() 以后,函数开始执行,这时先打印 "I am yangyanxing", 之后遇到 yield 关键字,此时函数又被中断,脚本执行结束,程序只打印了一行 "I am yangyanxing",如果想要打印出 I am fjy 呢,以时需要再调用一次 c.next(), 当再次调用 c.next() 时,函数从之前的 yield 处开始执行,由于函数在之后没有 yield 了,所以程序会抛一个 StopIteration 异常。 与 next() 函数相关的还有一个 send() 函数,next 函数传递的是 None , send 函数可以传递对应的值。其实next()和send()在一定意义上作用是相似的,区别是send()可以传递yield表达式的值进去,而next()不能传递特定的值,只能传递None进去。因此,我们可以看做c.next() 和 c.send(None) 作用是一样的。看如下的代码。

函数的输出为:

这代码解析,首先通过 c = h() 定义了一个 generator ,然后调用 c.next() 启动这个生成器,生成器先打印出 I am yangyanxing 然后遇到 m = yield 5 这行代码,后停止,之后再调用 c.send("hahaha") ,这时候 m 的值就是 hahaha, 然后再打印出 m ,之后再打印出 I am fjy,之后又遇到了 yield 关键字,程序又中止了,整个脚本执行结束,需要提醒的是,第一次调用时,请使用next()语句或是send(None),不能使用send发送一个非None的值,否则会出错的,因为没有yield语句来接收这个值。那么 next() 与 send() 函数的返回值么呢? 注意到上面函数中的 yield 之后是一个5了吗?其实这就是调用 netx 或者 send 以后得到的返回值。

得到的输出为:

异步使用

同步的困扰

首先看以下的代码,以下是在python2中编写

我分别用浏览器和和用脚本对 http://127.0.0.1:8000/?q=yangyanxing 该 url 进行访问,脚本如下:

服务端显示:

脚本打印为 7或者8

在同步应用中,由于同时只能提供一个请求。所以,如果一个路由中有一个比较耗时的操作,如代码中的 time.sleep(5) 那么意味着如果同时有两个请求,那么第二个请求只能等待服务器处理完第一个请求之后才能处理第二个请求,也就中处理两个请求,最短要5秒,最长要10秒,这还只是2个,如果有10个那就是要50秒处理完所有的请求,这个效率是无法接受的,服务端可否同时处理10个请求,就像文章一开始提到的同时做三件事情,在处理一个耗时的操作时,不是干等着这件事情处理完,而是去做别的事情,当那件事情结束以后,再通过调用回调函数来通知调用者。

异步的使用

客户端的实现

异步的使用可以分为客户端的调用与服务端的处理,先从简单的来看,客户端的调用,比如你要同时访问 baidu.com 10次,你会怎么做?可以依次的对 baidu 发起10次请求,每次请求结束以后再发起下一次请求,假如每次请求是1秒钟,那么10次请求至少要用10秒钟,排除IO相关耗时,代码可能是这个样子的:

执行结果如下:

一共用了0.7秒,百度的反应可能太快了,我们准备一个本地的环境来模拟慢返回。这里我先使用tornado的异步协程处理,之后再详细说明该处的用法。

请求代码改为三次,只是为了说明问题

结果:

可以看到,总是时间是15秒,同步对一个url发请求,在没有做异步处理的时候时间是累积的。接下来说本篇的重点,协程。

定义协程

在一个普通的函数前面加上 async 关键字,此时该函数会返回一个coroutine对象,函数里也不会立刻执行.

运行结果:

此处的 s 是一个coroutine对象,那么怎么才能执行函数里面的方法呢? 将这个协程对象放到事件循环 event_loop 中执行

执行结果:

如果同时发三个请求呢?这里要对协程做一个封装,将其封装成一个 task 对象

结果如下:

总的时间还是15秒,并没有实现异步呢,还是同步的依次执行请求。

其实,要实现异步处理,我们得先要有挂起的操作,当一个任务需要等待 IO 结果的时候,可以挂起当前任务,转而去执行其他任务,这样我们才能充分利用好资源,上面方法都是一本正经的串行走下来,连个挂起都没有,怎么可能实现异步?

上面的函数存在耗时的操作就是r = requests.get(url) 那么将它写成挂起的呢?

r = await requests.get(url)

不出意外的报错了

这个错误的意思是 requests 返回的 Response 对象不能和 await 一起使用,await 后面的对象必须是如下格式之一

原生 coroutine 对象一个由 types.coroutine() 修饰的生成器,这个生成器可以返回 coroutine 对象。一个包含 __await 方法的对象返回的一个迭代器。 reqeusts 返回的 Response 不符合上面任一条件,因此就会报上面的错误了。

既然 await 后面的对象要是 coroutine 对象 ,那么将其包装在async 后面不就可以了吗?

这次不报错了,但是依然没有异步的执行

也就是说我们仅仅将涉及 IO 操作的代码封装到 async 修饰的方法里面是不可行的!我们必须要使用支持异步操作的请求方式才可以实现真正的异步,所以这里就需要 aiohttp 派上用场了。

aiohttp 是一个支持异步请求的库,利用它和 asyncio 配合我们可以非常方便地实现异步请求操作。

执行结果:

这次终于实现了异步请求。

还记得最开始的洗衣做饭的例子吗?可以使用异步协程来实现,代码大概是这个样子

执行结果:

服务端的实现

先看下tornado在python2中的解决方案.

我们再来翻过头来看之前用tornado写的服务端同步代码

在 IndexHandler 中的 get 方法,由于当中存在了一个比较耗时的操作,time.sleep(5) 处理完这个请求需要卡5秒,在卡住的这段时间,tornado无法再完成别的请求,如果此时再发来一个 / 的请求,那么只能等待这前的请求操作结束之后再对处理新发过来的请求,如果同时有1万个请求发过来,可想而知,最后一个请求就等到猴年马月才能处理完呢……

解决方法是使用@tornado.web.asynchronous 和@tornado.gen.coroutine 装饰器,将耗时的操作放到线程中去执行,这里的耗时操作 time.sleep(5) 是阻塞的,所以将阻塞函数放加上 @run_on_executor 装饰器

注意到在 IndexHandler 中有一行初始化 executor 的代码 executor = ThreadPoolExecutor(100) 这里的参数100是最大的线程数,我这里传的是100,也就意味着同时能处理100个请求,当有101个请求的时候,前100个请求可以同时在2秒内执行,最后的那一个请求就要等之前有结束的线程以后再去执行了。

再看下tornado在python3.5 中的解决方案

由于在python3.5以后引入了 asyncio这个标准库,很多异步的操作可以用这个库来操作

IndexHandler 中的 get 方法使用了async 与await 关键字来达到异步的处理请求,这里的asyncio.sleep(5) 是异步的暂停5秒,如果此处的方法涉及到无法使用异步请求的库该怎么处理,比如说我就想使用time.sleep(5) 则需要在线程池中运行,就像上面的/ 路由里使用 @run_on_executor 中执行。

结语

异步操作涉及的知识点比较多,不同版本的 python 对于异步的处理也不一样,有些东西如 yield 理解起来比较费劲,需要多在项目中实践,tornado 这个框架的设计初衷也是异步网络库,过使用非阻塞网络I/O, Tornado 可以支持上万级的连接,所以要使用过程中要多多考虑异步非阻塞的编码。

最后感谢每一个认真阅读我文章的人,礼尚往来总是要有的,这些资料,对于【软件测试】的朋友来说应该是最全面最完整的备战仓库,虽然不是什么很值钱的东西,如果你用得到的话可以直接拿走:

这些资料,对于【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴上万个测试工程师们走过最艰难的路程,希望也能帮助到你! 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/662533.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[香橙派开发系列]使用蓝牙和手机进行信息的交换

文章目录 前言一、HC05蓝牙模块1.HC05概述2.HC05的连接图3.进入HC05的命令模式4.常用的AT指令4.1 检查AT是否上线4.2 重启模块4.3 获取软件版本号4.4 恢复默认状态4.5 获取蓝牙的名称4.6 设置蓝牙模块的波特率4.7 查询蓝牙的连接模式4.8 查询模块角色 5.连接电脑6.通过HC05发送…

2024美赛MCM Problem A: Resource Availability and Sex Ratios资源可用性和性别比例 完整代码以及思路分享

虽然一些动物物种存在于通常的雄性或雌性性别之外,但大多数物种实质上是雄性或雌性。虽然许多物种在出生时的性别比例为1:1,但其他物种的性别比例并不均匀。这被称为适应性性别比例的变化。例如,美洲短吻鳄孵化卵的巢穴的温度会影…

sql指南之null值用法

注明:参考文章: SQL避坑指南之NULL值知多少?_select null as-CSDN博客文章浏览阅读2.9k次,点赞7次,收藏21次。0 引言 SQL NULL(UNKNOW)是用来代表缺失值的术语,在表中的NULL值是显示…

Git解决分支合并冲突的问题:分支合并提交出现了不同分支同一个文件的修改的冲突解决

有些时候我们合并分支的时候,会出现冲突,原因就是我们修改了分支A 的第一行代码,我们还修改了主分支上面同一个文件中的第一行代码(分支A已经提交过一次),此时我们在次合并的时候就会出现冲突,需…

智慧养老成关键力量,全视通智慧机构养老解决方案来助力

近日,国新办举行国务院政策例行吹风会,国家发展改革委、工业和信息化部、民政部、商务部有关负责人介绍《关于发展银发经济增进老年人福祉的意见》有关情况并答记者问。 会上,民政部养老服务司负责人李永新透露,将发展智慧养老服务…

tidb节点重启后,服务无法重连

大家好,我是烤鸭: 前几天遇到tidb节点重启后服务无法重连,确切地说是两个服务,一个可以正常重连,一个不行。 问题复现 由于线上执行慢SQL,导致TiDB 单个节点宕机重启。 其中A服务的3个节点和B服务的1个节…

Sketch使用手册:从入门到精通的完整教程

Sketch软件是Mac平台上流行的矢量图形编辑软件,旨在帮助用户创建网站、移动应用、图标等各种设计原型。Sketch软件的设计风格简洁明了,界面操作简单易用,非常适合UI/UX设计师、平面设计师等数字创意人员。本文将根据如何使用Sketch&#xff0…

IEPE数据采集卡的作用说明

IEPE传感器是一种特殊的加速度传感器,其特点是自带电量放大器或电压放大器。这种传感器产生的电信号非常微弱,很容易受到噪声干扰,因此需要使用灵敏的电子器件进行放大和信号调理。为了实现更好的抗噪声性能和更方便的封装,IEPE传…

xmind思维导图 for mac v24.01中文版

mac电脑上思维导图软件哪个好呢? xmind for mac一个功能强大、易于使用的思维导图软件,够帮助你更好地组织思维、管理信息、规划项目和解决问题,提高个人和团队的工作效率。 软件下载:xmind思维导图 for mac v24.01中文版 XMind f…

数据可视化工具选择指南:六款主流工具的综合评测

随着大数据时代的来临,数据可视化已成为各行业不可或缺的工具。本文将为您介绍市面上六款主流数据可视化工具,包括山海鲸可视化、Echarts、D3.js、Tableau、Power BI和Funnel.io,帮助您更好地了解并选择适合您的工具。 山海鲸可视化 山海鲸…

【Django开发】美多商城项目第2篇:Django用户注册和登录开发(附代码,已分享)

本系列文章md笔记(已分享)主要讨论django商城项目相关知识。项目利用Django框架开发一套前后端不分离的商城项目(4.0版本)含代码和文档。功能包括前后端不分离,方便SEO。采用Django Jinja2模板引擎 Vue.js实现前后端…

C# .Net学习笔记—— 异步和多线程(异常处理)

一、异常处理 1、下面for循环20个线程&#xff0c;到11&#xff0c;12号的时候执行失败&#xff0c;这里我也用了try catch来捕获异常。 private void button11_Click(object sender, EventArgs e){TaskFactory taskFactory new TaskFactory();List<Task> taskList ne…

回归预测 | Matlab基于POA-LSSVM鹈鹕算法算法优化最小二乘支持向量机的数据多输入单输出回归预测

回归预测 | Matlab基于POA-LSSVM鹈鹕算法算法优化最小二乘支持向量机的数据多输入单输出回归预测 目录 回归预测 | Matlab基于POA-LSSVM鹈鹕算法算法优化最小二乘支持向量机的数据多输入单输出回归预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 Matlab基于POA-LSSVM…

【数据结构与算法】之排序系列-20240201

【数据结构与算法】之排序系列-20240201 一、88. 合并两个有序数组二、169. 多数元素三、217. 存在重复元素四、242. 有效的字母异位词五、268. 丢失的数字六、349. 两个数组的交集七、350. 两个数组的交集 II 一、88. 合并两个有序数组 简单 给你两个按 非递减顺序 排列的整数…

【Uni-App】运行微信小程序时报错routeDone with a webviewId 2 that is not the current page

使用HBuilderX开发微信小程序&#xff0c;运行项目的时有可能会出现routeDone with a webviewId 1 that is not the current page的报错&#xff0c;但不影响运行。如果强迫症介意的话&#xff0c;可以考下面的方法进行修复。 产生原因 由于微信开发者工具的调试基础库处于灰度…

私域流量如何变现?一站式产品体系搭建与运营策略大公开

在互联网日新月异的今天&#xff0c;我们面临着人口红利逐渐消失的问题&#xff0c;各行各业的广告投放获客成本也在不断上涨。为了降低成本并提高用户转化和复购率&#xff0c;我们需要寻找新的解决方案。此时&#xff0c;私域流量的概念应运而生&#xff0c;成为了一个值得考…

【计算机二级考试C语言】C递归

目录 C 递归 数的阶乘 实例 斐波那契数列 实例 C 递归 递归指的是在函数的定义中使用函数自身的方法。 举个例子&#xff1a; 从前有座山&#xff0c;山里有座庙&#xff0c;庙里有个老和尚&#xff0c;正在给小和尚讲故事呢&#xff01;故事是什么呢&#xff1f;"从…

Python 的 pass 语句到底是什么?

Python 中的 pass 语句是一个简单的概念&#xff0c;即使没有编程经验的初学者也能很快掌握。官方文档提供了非常简单的介绍&#xff0c;下面的三个示例可以让我们快速了解如何使用它。 pass 本质上是一个空操作&#xff0c;除了允许解释器在跳过语法之前检查语法是否有效之外&…

apk反编译修改教程系列---修改apk的默认颜色 布局颜色 手机电脑同步演示【十】

往期教程&#xff1a; apk反编译修改教程系列-----修改apk应用名称 任意修改名称 签名【一】 apk反编译修改教程系列-----任意修改apk版本号 版本名 防止自动更新【二】 apk反编译修改教程系列-----修改apk中的图片 任意更换apk桌面图片【三】 apk反编译修改教程系列---简单…

PMP备考的三个阶段及学习方法分享

PMP证书是项目管理必备的关键技能证书&#xff0c;是具备进行项目管理的重要技能体现。无论升职加薪&#xff0c;还是从事项目管理工作&#xff0c;都非常重要。 个人主要从事产品开发工作&#xff0c;开始逐渐承担一些项目经理角色&#xff0c;但目前项目管理知识薄弱&#x…