fastapi 下怎么正确使用 async和await?

声明:异步“请求” 和 异步“方法调用” 的区别

问题
如果使用uvicorn单进程、单线程启动fastapi服务,其中代码里:

@app.get("/bb")
async def read_item():await long_running_task(4)result = await long_running_task(4)return {"result": result}

这时,/bb的单次请求时,执行await long_running_task(4)任务先等待4s,结束后再次执行long_running_task(4)又是4s等待,总共就是8s,也就是在方法体内的await是串行的。

那当多次请求/aa时:

@app.get("/aa")
async def read_item():result = await long_running_task()return {"result": result}

这时,fastapi 会把2个异步请求分别转化为task,而不是coroutine了,并把task提交到事件循环中,即每个请求都是1个独立的任务
事件循环会并发地处理这些任务,因此每个请求的 await long_running_task(4) 调用会并发执行。
因此,每个请求的响应时长是 4 秒,而不是顺序累加成 8 秒。

【关键点】

1、 方法体内多个await :在同一个方法体内,不管有多少个“await ” ,它们都共享同一个任务。
await是并发 or 串行取决于在await执行前,是否存在多个已启动的task。【具体详见: 异步编程下await的理解】

2、多个请求并发处理:当多个请求并发时,每个请求会被包装成一个独立的任务,并提交到事件循环中。事件循环会并发地处理这些任务,因此每个请求的await 调用会并发执行。

1、同步、异步方法 + 同步阻塞

from fastapi import FastAPI, Query, Request, Response
import uvicorn
import time
import sys
import os
import asynciosys.path.append(os.getcwd())
app = FastAPI(title="bbb")
#***************************************1、同步阻塞操作... ***************************************
### def不用async修饰:同步方法+同步阻塞 ### 
@app.get("/aa")
def aa():print("aa")time.sleep(3)  # 同步阻塞3sprint("aaa")return "aaaaaa"### 异步方法+同步阻塞 ### 
@app.get("/bb")
async def bb(): ''''虽然bb用async关键字修饰成了一个异步函数(协程),但该方法内部使用了同步阻塞,所以导致它的异步能力失效,变成同步方法!!'''print("bb")time.sleep(8)  # 是一个同步阻塞操作。它会阻塞当前线程,直到指定的秒数过去。print("bbb")return "bbbbbb"#***************************************2、非阻塞操作... ***************************************
### 异步方法 + 无阻塞 ### 
@app.get("/hello")
async def hello(con: str = Query(..., description="输入字符串")):print("hello")con = con + "**** 好样的!"return {"message": con}### 同步方法 + 无阻塞 ### 
@app.get("/hello2")
def hello2(con: str = Query(..., description="输入字符串")):con = con + "**** 好样的222222!"return {"message": con}if __name__ == "__main__":uvicorn.run(app,host="0.0.0.0",port=9351)  

1.1 仅同步请求的并发

fastapi 在处理同步请求时,会把他们放入底层维护的工作线程池中,每个请求由AnyIO worker thread(工作线程)执行。

测试

/aa 和 /hello2 并发:
/aa耗时3.02s, /hello2耗时5ms。

结论:/aa 的阻塞不会影响 /hello2 的及时返回。

1.2 仅异步请求的并发

fastapi 在处理异步请求时,会把他们放入Main Thread的协程event_loop中。如果一个异步方法里存在同步阻塞,那他会导致后请求的异步方法也被阻塞!
因为所有的异步都在1个event_loop中~~

测试

/bb 和 /hello 并发:
/hello要等/bb执行完才会结束,总体耗时8s。

结论:/bb 的阻塞会影响 /hello 的及时返回。

1.3 同步请求 和 异步请求 的并发

(1)/aa稍早于/bb

通过调用堆栈可以看到:
/aa即使阻塞也不会阻塞/bb的请求,两者可同步执行方法体!

但是,不知道为什么有时候/aa要等到/bb响应结束也才return, 也就是/aa的总响应时长不总是3s附近,会出现return bbbbbb结束后才收到return aaaaaa,导致/aa的响应时长大于8s!
执行结果:
aa
bb
aaa
bbb

(2)/bb稍早于/aa

通过调用堆栈可以看到:
/bb阻塞会导致/aa不能立即执行,而是要等待/bb完全执行完才会进入/aa的方法体!

执行结果:
bb
bbb
aa
aaa

2、异步方法阻塞的解决方案

2.1 使用线程池执行同步阻塞

from fastapi import FastAPI, Query, Request, Response
import uvicorn
from concurrent.futures import ThreadPoolExecutor
import time
import sys
import os
import asynciosys.path.append(os.getcwd())
app = FastAPI(title="bbb")threadpool=ThreadPoolExecutor(max_workers=3)############## 【ok】放线程池中:不会阻塞主线程~ ##################
@app.get("/ver2")
async def ver2(request:Request):msg=request.query_params.get("msg")loop=asyncio.get_event_loop()  # 拿到主线程的事件循环(事件循环可以看做“方法间”的异步)task={"msg":msg}def handle_task():print("task recieved:", task["msg"])result=task["msg"].lower()time.sleep(8)return result# 在主线程的事件循环里等待异步结果,因为事件循环是针对“所有方法间”的,所以是在主线程里result=await loop.run_in_executor(threadpool, handle_task)  print("task ends:", result, asyncio.get_event_loop)return Response(result)if __name__ == "__main__":uvicorn.run(app,host="0.0.0.0",port=9351)  

2.2 使用await 异步等待

2.2.1 异步方法内部使用同步阻塞

【方法间】:A、B、C、D四个请求在异步执行,遵循单线程下的协程异步;
【方法内】:存在同步阻塞,导致遇到阻塞时不会主动让出线程,必须等待当前方法整体执行完(包括先阻塞结束,再执行该方法体内阻塞后的代码)才会让出线程。

2.2.2 异步方法内部使用异步阻塞

【方法间】:不变
【方法内】:异步阻塞,当某个请求调用执行到异步调用代码(await)时,会主动让出线程,失去抢占线程的资格,由其他异步方法抢占。
(1)抢占到线程的协程(异步请求下的异步方法)继续这种操作,即遇到异步阻塞就让出线程;
(2)一旦某个方法异步阻塞结束,它就会恢复重新抢占线程的资格;
(3)当某个方法体全部执行完后,就独立返回,不受其他未完成的方法影响。

from fastapi import FastAPI
import uvicorn
# from concurrent.futures import ThreadPoolExecutor
import time
import sys
import os
import asynciosys.path.append(os.getcwd())
app = FastAPI(title="ttt")@app.get("/a")
async def A(a: int):# 任务1a = a+10print(f"A任务1:{str(a)}")# 任务2a = a+1print(f"A任务2:{str(a)}")# 任务3# time.sleep(8)  # 同步阻塞8sawait asyncio.sleep(8)print(f"A任务3:8s睡好了")# 任务4a = a+1print(f"A任务4:{str(a)}")@app.get("/b")
async def B(a: int):# 任务1a = a+20print(f"B任务1:{str(a)}")# 任务2a = a+1print(f"B任务2:{str(a)}")# 任务3# time.sleep(4)  # 同步阻塞4sawait asyncio.sleep(4)print(f"B任务3:4s睡好了")# 任务4a = a+1print(f"B任务4:{str(a)}")@app.get("/c")
async def C(a: int):# 任务1a = a+30print(f"C任务1:{str(a)}")# 任务2a = a+1print(f"C任务2:{str(a)}")# 任务3a = a+1print(f"C任务3:{str(a)}")# 任务4a = a+1print(f"C任务4:{str(a)}")@app.get("/d")
async def D(a: int):# 任务1a = a+40print(f"D任务1:{str(a)}")# 任务2a = a+1print(f"D任务2:{str(a)}")# 任务3a = a+1print(f"D任务3:{str(a)}")# 任务4a = a+1print(f"D任务4:{str(a)}")if __name__ == "__main__":uvicorn.run(app,host="0.0.0.0",port=9351)

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

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

相关文章

无人机之基本结构篇

无人机(Unmanned Aerial Vehicle, UAV)作为一种无人驾驶的飞行器,其基本结构涵盖了多个关键组件,这些组件共同协作以实现无人机的自主飞行和执行各种任务。以下是无人机基本结构的详细解析: 一、飞机平台系统 机身&am…

C++中 inline 的含义是什么?

在C中,inline是一个关键字,它向编译器发出一个请求(注意,这是一个请求而不是命令),请求编译器尝试将函数的调用替换为函数体本身的代码。这样做的目的是减少函数调用的开销,特别是对于那些体积小…

vue2表单校验:添加自定义el-form表单校验规则

前言 在vue2表单校验:el-form表单绑定数组并使用rules进行校验_vue2 rules校验-CSDN博客中,使用form原生的rules对表单中每个控件的必填、格式等做了校验。但是保存时,除了验证每一个控件的输入合乎要求外,还需要验证控件之间的数…

SpringBoot集成kafka-生产者发送消息

springboot集成kafka发送消息 1、kafkaTemplate.send()方法1.1、springboot集成kafka发送消息Message对象消息1.2、springboot集成kafka发送ProducerRecord对象消息1.3、springboot集成kafka发送指定分区消息 2、kafkaTemplate.sendDefault()方法3、kafkaTemplate.send(...)和k…

WIN/MAC 图像处理软件Adobe Photoshop PS2024软件下载安装

目录 一、软件概述 1.1 基本信息 1.2 主要功能 二、系统要求 2.1 Windows 系统要求 2.2 macOS 系统要求 三、下载 四、使用教程 4.1 基本界面介绍 4.2 常用工具使用 4.3 进阶操作 一、软件概述 1.1 基本信息 Adobe Photoshop(简称PS)是一款…

springboot嵌入式数据库实践-H2内嵌数据库(文件、内存)

本文章记录笔者的嵌入式数据库简单实现, 记录简要的配置过程。自用文章,仅作参考。 目录 本文章记录笔者的嵌入式数据库简单实现, 记录简要的配置过程。自用文章,仅作参考。 嵌入式数据库 -------------------------------具…

前端手写源码系列(三)——手写_deepClone深浅拷贝

目录 一、基本类型和引用类型二、深浅拷贝概念三、浅拷贝实现方式1、Object.assign()2、Array.prototype.concat() 修改新对象会改到原对象3、Array.prototype.slice() 四、深拷贝实现方式1、JSON.parse(JSON.stringify())2、手写递归方法3、函数库lodash 五、手写深拷贝 一、基…

Linux系统(centos7)增加一个开机自启任务

任务背景 已经上线了一个java的springboot项目,使用start.sh脚本进行启动,脚本内容为: #!/bin/bashnohup java -jar /opt/javaProject/PracticeSpring-0.0.1-SNAPSHOT.jar > /opt/javaProject/run.log 2>&1 & 现在&#xff…

16岁激活交学费银行卡需要本人实名电话卡,线下营业厅不给办,怎么办?

16岁激活交学费银行卡需要本人实名电话卡,线下营业厅不给办,怎么办? 话卡办理规定: 根据《民法典》和《电话用户真实身份信息登记规定》的相关要求,未满16周岁的用户通常需要在监护人的陪同下办理电话卡,并…

uniapp微信小程序 分享功能

uniapp https://zh.uniapp.dcloud.io/api/plugins/share.html#onshareappmessage export default {onShareAppMessage(res) {if (res.from button) {// 来自页面内分享按钮console.log(res.target)}return {title: 自定义分享标题,path: /pages/test/test?id123}} }需要再真机…

IntelliJ IDEA智能代码补全​和集成AI助手说明及操作

IntelliJ IDEA的智能代码补全和集成AI助手是开发者提高编码效率和代码质量的重要工具。以下是对这些功能的详细说明及操作指南: 一、智能代码补全 1. 功能说明 IntelliJ IDEA的智能代码补全功能利用先进的算法和上下文分析,为开发者提供准确、快速的代…

程序猿必备技能-Bat脚本

Batch 脚本(批处理脚本)是在 Windows 操作系统中使用的一种脚本语言,用于自动化执行一系列命令。Batch 脚本是由 .bat 或 .cmd 文件扩展名标识的文本文件,这些文件可以被 Windows 的命令行解释器(如 cmd.exe&#xff0…

衡石科技BI的API如何授权文档解析

授权说明​ 授权模式​ 使用凭证式(client credentials)授权模式。 授权模式流程说明​ 第一步,A 应用在命令行向 B 发出请求。 第二步,B 网站验证通过以后,直接返回令牌。 授权模式结构说明​ 接口说明​ 获取a…

shell之getopts

getopts 是一个常用于解析命令行选项的bash内建命令。它的基本语法是: getopts optstring name [arg...]optstring列出了对应的Shell Script可以识别的所有参数。比如: 如果 Shell Script可以识别-a,-f以及-s参数,则optstring就是…

【贪心 决策包容性 】757. 设置交集大小至少为2

本文涉及知识点 贪心 决策包容性 LeetCode757. 设置交集大小至少为2 给你一个二维整数数组 intervals ,其中 intervals[i] [starti, endi] 表示从 starti 到 endi 的所有整数,包括 starti 和 endi 。 包含集合 是一个名为 nums 的数组,并…

Quasar V2.16.4 新版发布,基于 Vue 3 的前端开发框架,一套代码发布到多端

Quasar 又发布新版本了,性能优秀的 Vue 组件开发框架,时隔3年再次推荐给大家。 早在2021年,我就写了一篇简单的文章向大家推荐了 Quasar 这款 Vue.js 开发框架,如今3年过去了,Quasar 发展得很好,更新频率依…

H5开发有哪些技巧?

随着现代社会的飞速发展,网页开发已经从传统的HTML、CSS、JavaScript往H5发展。H5也称为HTML5,可以理解为是HTML的升级版,具有更加优秀的性能、更加完善的功能和更加多样的体验。因其灵活性和跨平台特性,成为了各类移动应用和网页…

数学建模学习(128):使用Python结合CILOS与熵法的多准则决策权重确定

本文介绍方法为:结合CILOS与熵法的多准则决策权重,请理解为主,代码可以当作模板使用。 文章目录 1 引言2 问题背景2.1. 熵法 (Entropy Method)2.2 准则影响损失法 (CILOS Method)2.3 Python代码实现2.4 结果的决策指导意义2.4 结论参考文献1 引言 多准则决策(Multi-Criter…

面试常问! transformer中dk的大小,以及为什么设成这样,维度,原文分析。

目录: 原文 :翻译:流程:原因: 原文(多头注意力部分) : 李沐b站论文精读 论文网盘下载:链接 提取码: vm3d 翻译: 在这项工作中,我们采用了 h8 个并行注意力层&#xff…

flutter之image_picker上传图片

原文地址 image_picker 安装 image_picker: ^1.1.2使用 我们获取到上传的照片后,将其转为base64编码的格式,方便后续使用 // source: 接收两种模式,相册和拍照final pickedImage =await