python中的异步实践与tornado应用

news2025/2/4 1:15:58

最近项目中由于在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.coloradmin.cn/o/1427393.html

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

相关文章

求n的k次方

递归法&#xff1a; #include<stdio.h> int mi(int n, int k) {if (k 1) //如果是1次方{return n; //返回n的1次方&#xff08;也就是n&#xff09;}else{return n * mi(n, k - 1); //n*n的k-1次方} } int main() {int n 0, k 0, sum 0;printf("请输入n值…

你和年薪百万检验工程师之间差了一个TA?

在研发周期紧、并行项目数直线上涨的背景下 试制样品测试-检核报告撰写-优化决策分析&#xff0c; 每一步都让工程师苦之久矣。 缺乏体系管理 工程师群里被猛的崩溃时刻 测试主管&#xff1a;检测方案依据的行业规范文件是哪个&#xff1f;测试少了这个参数&#xff0c;检测…

视频融合平台EasyCVR推流成功但平台显示不在线是什么原因?

TSINGSEE青犀视频监控汇聚平台EasyCVR可拓展性强、视频能力灵活、部署轻快&#xff0c;可支持的主流标准协议有国标GB28181、RTSP/Onvif、RTMP等&#xff0c;以及支持厂家私有协议与SDK接入&#xff0c;包括海康Ehome、海大宇等设备的SDK等。平台既具备传统安防视频监控的能力&…

如何改进YOLOv5主干网络

D:\yolov5-master\models目录下新建mobilevit.py文件夹 代码内容&#xff1a; import torch import torch.nn as nn from einops import rearrange def conv_1x1_bn(inp, oup):return nn.Sequential(nn.Conv2d(inp, oup, 1, 1, 0, biasFalse),nn.BatchNorm2d(oup),nn.SiLU()) …

Python库Bleach:保护应用免受XSS攻击

Python库Bleach&#xff1a;保护应用免受XSS攻击 在当今的网络环境中&#xff0c;跨站脚本攻击&#xff08;XSS&#xff09;是一种常见而严重的安全威胁。为了保护我们的应用程序免受XSS攻击&#xff0c;我们可以使用Python库Bleach。本文将介绍Bleach库的基本概念、功能和用法…

物联网浏览器(IoTBrowser)-Modbus协议集成和测试

Modbus协议在应用中一般用来与PLC或者其他硬件设备通讯&#xff0c;Modbus集成到IoTBrowser使用串口插件模式开发&#xff0c;不同的是采用命令函数&#xff0c;具体可以参考前面几篇文章。目前示例实现了Modbus-Rtu和Modbus-Tcp两种&#xff0c;通过js可以与Modbus进行通讯控制…

其他发现:开源数据可视化分析工具DataEase介绍文档

一、 简介 DataEase 是开源的数据可视化分析工具&#xff0c;帮助用户快速分析数据并洞察业务趋势&#xff0c;从而实现业务的改进与优化。DataEase 支持丰富的数据源连接&#xff0c;能够通过拖拉拽方式快速制作图表&#xff0c;并可以方便地与他人分享。 二、 优势 1、 开…

计算机视觉实战项目4(单目测距与测速+摔倒检测+目标检测+目标跟踪+姿态识别+车道线识别+车牌识别+无人机检测+A_路径规划+行人车辆计数+动物识别等)

基于YOLOv5的无人机视频检测与计数系统 摘要&#xff1a; 无人机技术的快速发展和广泛应用给社会带来了巨大的便利&#xff0c;但也带来了一系列的安全隐患。为了实现对无人机的有效管理和监控&#xff0c;本文提出了一种基于YOLOv5的无人机视频检测与计数系统。该系统通过使用…

UE4 C++ 数据表

//添加使用DataTable需要的头文件 #include "Engine/DataTable.h"//基于结构体变量类型&#xff0c;创建数据表DataTable类型 USTRUCT(BlueprintType) struct FMyDataTableStruct : public FTableRowBase //把结构体变量公开到数据表类型 {GENERATED_BODY() //必须添…

前后端分离,RSA加密传输方案

1.原理 RSA是一种非对称加密算法。通过生成密钥对&#xff0c;用公钥加密&#xff0c;用私钥解密。对于前后端分离的项目&#xff0c;让前端获取到公钥对敏感数据加密&#xff0c;发送到后端&#xff0c;后端用私钥对加密后的数据进行解密即可。 2.实现 RSA工具类&#xff1…

MQ回顾之rabbitmq速通

rabbitMQ相对来说功能比较完善&#xff0c;吞吐量会低一点。 持续更新…… 安装 docker 测试选择docker安装 官方安装操作 1、docker pull rabbitmq:latest 2、docker run -d --hostname my-rabbit --name some-rabbit -p 15672:15672 -p 5672:5672 rabbitmq 3、docker…

C/C++ C++入门

个人主页&#xff1a;仍有未知等待探索-CSDN博客 专题分栏&#xff1a;C_仍有未知等待探索的博客-CSDN博客 目录 一、C关键字 二、命名空间 1、区别 1. C语言 ​编辑 2. C 2、命名空间定义 3、命名空间的使用 三、C输入&输出 四、缺省参数 五、函数重载 六、引用 …

80.如何评估一台服务器能承受的最大TCP连接数

文章目录 一、一个服务端进程最多能支持多少条 TCP 连接&#xff1f;二、一台服务器最大最多能支持多少条 TCP 连接&#xff1f;三、总结 一个服务端进程最大能支持多少条 TCP 连接&#xff1f; 一台服务器最大能支持多少条 TCP 连接&#xff1f; 很多朋友可能第一反应就是端…

【RT-DETR有效改进】利用YOLO-MS的MSBlock模块改进ResNet中的Bottleneck(RT-DETR深度改进)

👑欢迎大家订阅本专栏,一起学习RT-DETR👑 一、本文介绍 本文给大家带来的改进机制是利用YOLO-MS提出的一种针对于实时目标检测的MSBlock模块(其其实不能算是Conv但是其应该是一整个模块),我们将其用于替换我们ResNet中Basic组合出一种新的结构,来替换我们网络中的…

Spring:JDBCTemplate 的源码分析

一&#xff1a;JdbcTemplate的简介 JdbcTemplate 是 Spring Template设置模式中的一员。类似的还有 TransactionTemplate、 MongoTemplate 等。通过 JdbcTemplate 我们可以使得 Spring 访问数据库的过程简单化。 二&#xff1a;执行SQL语句的方法 1&#xff1a;在JdbcTempla…

智能末世战争之机器人的反击

在遥远的未来&#xff0c;地球陷入了一场空前的战争。这场战争不同于以往的任何战争&#xff0c;因为这是由人工智能和机器人主导的战争。在战争爆发之前&#xff0c;人类一直依赖AI和机器人来提高生产效率和生活质量。然而&#xff0c;随着AI技术的飞速发展&#xff0c;机器人…

H5 简约四色新科技风引导页源码

H5 简约四色新科技风引导页源码 源码介绍&#xff1a;一款四色切换自适应现代科技风动态背景的引导页源码&#xff0c;源码有主站按钮&#xff0c;分站按钮2个&#xff0c;QQ联系站长按钮一个。 下载地址&#xff1a; https://www.changyouzuhao.cn/11990.html

flinkcdc 3.0 尝鲜

本文会将从环境搭建到demo来全流程体验flinkcdc 3.0 包含了如下内容 flink1.18 standalone搭建doris 1fe1be 搭建整库数据同步测试各同步场景从检查点重启同步任务 环境搭建 flink环境(Standalone模式) 下载flink 1.18.0 链接 : https://archive.apache.org/dist/flink/flink…

【大数据】专业融合型人才迎来发展良机-国家数据局正式揭牌

⭐简单说两句⭐ 作者&#xff1a;后端小知识 CSDN个人主页&#xff1a;后端小知识 &#x1f50e;GZH&#xff1a;后端小知识 &#x1f389;欢迎关注&#x1f50e;点赞&#x1f44d;收藏⭐️留言&#x1f4dd; 摘要&#xff1a; 新华社北京10月26日电 《中国证券报》26日刊发文章…

shell - 正则表达式和grep命令和sed命令

一.正则表达式概述 1.正则表达式定义 1.1 定义 使用字符串描述、匹配一系列符合某个规则的字符串 1.2 了解 普通字符&#xff1a; 大小写字母、数字、标点符号及一些其它符号元字符&#xff1a; 在正则表达式中具有特殊意义的专用字符 1.3 层次分类 基础正则表达式扩展正…