FastAPI Web框架教程 第12章 异步async-await

12-1 fastapi是异步Web框架

从本教程开篇,我们就说FastAPI这个web框架是异步框架,那它到底是如何体现异步的呢?

想要学习使如何使用FastAPI的异步功能,那就必须要先了解什么是异步,什么是asyncio、async/await

【基础补充】

关于异步编程、协程实行的异步编程的基础知识

【重要结论】

本质上,实现异步的方式有三种:多进程、多线程和协程,FastAPI实现异步使用了多线程(线程池)和协程的方式

  • 当我们写普通形式的代码时:即使用 def 定义路径函数,FastAPI内部帮我们使用多线程(线程池)实现异步并发
  • 当我们写async形式的代码时:即使用 async def定义路径函数,FastAPI内部使用协程的方式实现异步并发。
  • 在一个项目中,我们可以同时普通函数定义的路径函数(api),和async def 形式定义的路径函数(api)
  • 但是要记得,在async def 形式定义的函数内,不能使用同步的代码,否则接口的响应速度极慢。

12-2 比较同步和异步代码

在FastAPI中可以使用普通函数定义的接口,也可以使用async def 实行定义的接口。但是使用是需要注意,否则会导致程序极慢。

示例1:普通函数形式定义的接口,会按照多线程(线程池)的方式异步执行

  • 比如这个接口,使用time.sleep(5)模拟耗时5s
  • 当两个客户端同时访问该接口时,服务端就会收到两个请求,因为FastAPI内部使用多线程的方式来处理,一个请求会被一个线程来处理,两个线程几乎同时执行。于是5秒后,两个请求都得到了响应。
import time
from fastapi import FastAPI

app = FastAPI()


@app.get("/")
def index():
    time.sleep(5)
    return "index"

示例2:async def 形式的定义的接口,使用单线程协程的形式异步执行

  • 因为使用了协程的方式,当有两个客户端发请求时,单线程内代码块级别的切换,最终5秒后两个客户端都会得到响应。

  • 注意:协程时不能使用同步阻塞的time模块,需要使用asyncio.sleep()。又因为它是协程对象,所以需要使用await才能被执行。

  • 另外,await必须使用在async定义的函数内,否则报错。

  • 同时注意,在async def 内部不能使用同步模块,否则就会编程单线程同步执行的方式。

import asyncio
from fastapi import FastAPI

app = FastAPI()


@app.get("/")
async def index():
    await asyncio.sleep(5)
    return "index"

示例3:在协程函数内错误使用同步模块

  • 因为使用async def定义的函数,所以FadtAPI内部使用单线程的协程方式运行代码,所以当两个客户端发请求时,服务端只有一个线程处理两个请求。
  • 但因为是同步阻塞5秒,所以只能等第一个请求5秒后处理完,才能开始执行第二个请求。所以第二个请求等待了10秒才能得到响应。
import time
from fastapi import FastAPI

app = FastAPI()


@app.get("/")
async def index():
    time.sleep(5)		# 不能在async def 函数内使用同步阻塞模块。
    return "index"

【注意】

  • 演示上述代码时,不要在一个浏览器上开两个tab页,可能无法演示出效果。这是因为有些浏览器有设置,一个host之后一个连接,所以打开的两个tab也通用一个和服务端通信的连接,此时的效果就是第一个请求得到响应后才开始第二个响应。
  • 你可以使用两个不同的浏览器演示示例;或者开两个cmd窗口,使用 curl命令来发请求。

在这里插入图片描述

12-3 同步异步如何选择

FastAPI非常灵活,支持你写普通的函数,也支持你写async def 形式的函数,那到底该如何选择?

下面有几个原则,可以帮助大家做选择:

  • 你需要使用一个普通的,内部有IO等待的第三方库,此时使用 def
@app.get('/')
def results():
    results = some_library()
    return results
  • 你需要使用的一个第三方库,需要使用 await调用时,此时使用 async def
@app.get('/')
async def read_results():
    results = await some_library()
    return results
  • 你自己编写的工具库或者第三方工具库,代码执行过程中不涉及IO等待,此时直接调用即可,不用 await
  • 当你的函数内部只要有一处使用 await, 该函数必须使用 async def 定义
@app.get('/')
async def read_results():
    results1 = some_library1()
    results2 = await some_library()
    return results
  • 当你对协程/async/await/asyncio这些概念不清楚的时候,就使用普通函数。

  • 你需要使用的一个第三方库是同步库,但是你需要它支持协程异步,那需要自己使用线程池的方式运行,参考视频:

    • 线程池和协程混合实现并发案例

12-4 fastapi集成aiohttp

传统python代码中发请求(如爬虫)我们一般使用requests模块,但是这个模块是同步阻塞的。

所以在异步asyncio体系中,我们不再使用requests模块,一般会使用异步的 aiohttp 模块。(httpx)

  • 使用前现在安装
pip3 install aiohttp
  • 简介:异步支持客户端和服务端,功能强大。https://docs.aiohttp.org/en/stable/

示例1:基本使用

import asyncio
import aiohttp

# 协程函数
async def aiohttp_demo():
    # 获取一个连接session
    async with aiohttp.ClientSession() as session:
        # 基于连接发送一个get请求并获取像一个response
        async with session.get('http://www.baidu.com') as response:
            # 从response中获取响应的各种结果
            # 因为基于上下文管理器,所以出自动关闭连接
            print("Status:", response.status)
            print("Content-type:", response.headers['content-type'])

            html = await response.text()
            print("Body:", html[:15], "...")

     
# 获取事件循环,在事件循环中执行协程函数
# loop = asyncio.get_event_loop()
# loop.run_until_complete(aiohttp_demo())


# 上面两行代码的简写,Python3.7以后的版本才可以使用
asyncio.run(aiohttp_demo())

示例2:在fastapi中使用

import aiohttp
from fastapi import FastAPI


app = FastAPI(title="使用aiohttp")


@app.get("/")
async def baidu_index():
    async with aiohttp.ClientSession() as session:
        async with session.get('http://www.baidu.com') as response:
            return {
                "status": response.status,
                "content-type": response.headers['content-type'],
                "body": await response.text()
            }
  • 或者将爬取百度页面的代码封装成一个协程函数,在接口中直接调用该协程函数即可
import aiohttp
from fastapi import FastAPI


app = FastAPI(title="使用aiohttp")


async def baidu_html():
    async with aiohttp.ClientSession() as session:
        async with session.get('http://www.baidu.com') as response:
            return {
                "status": response.status,
                "content-type": response.headers['content-type'],
                "body": await response.text()
            }


@app.get("/")
async def index():
    return await baidu_html()		# 需要使用await 才能执行baidu_html 这个协程函数

12-5 fastapi集成aiomysql

在python中操作mysql我们通常使用pymysql作为数据库驱动,但是在异步的世界中我们使用aiomysql当驱动。

官网:https://aiomysql.readthedocs.io/en/latest/index.html

  • 安装aiomysql
pip3 install aiomysql

示例1:aiomysql简单使用

import aiomysql
from aiomysql.cursors import DictCursor

from fastapi import FastAPI


app = FastAPI(title="使用aiomysql")


async def aiomysql_demo():
    # 获取连接对象
    conn = await aiomysql.connect(
        host="127.0.0.1",
        port=3306,
        user="root",
        password="12345",
        db="db",
        cursorclass=DictCursor      # 返回字典格式的数据
    )
    # 创建游标
    cur = await conn.cursor()
    # 执行SQL
    await cur.execute("SELECT * from users;")
    # 获取SQL结果
    result = await cur.fetchall()
    # 关闭CURSOR
    await cur.close()
    # 关闭连接
    conn.close()

    return result


@app.get("/")
async def index():
    return await aiomysql_demo()

12-6 fastapi集成databases

在异步世界中操作数据库,比如MySQL,我们需要只用 aiomysql,且需要自己手写SQL语句。

使用其他类型的数据库,比如PostgreSQL,则需要基于asyncpg或aiopg。

那样的就有一个问题,当我们的应用需要换一个数据库时,就需要调整基础代码,使用不灵活。

此时出现了一个工具,它封装了不同类型的数据库,我们只需要在使用它提供的接口操作数据库就行了,而不用关心底层的数据库驱动,它就是encode出品的 databases

在这里插入图片描述

官网简介

  • Databases gives you simple asyncio support for a range of databases.

  • It allows you to make queries using the powerful SQLAlchemy Core expression language, and provides support for PostgreSQL, MySQL, and SQLite.

  • Databases is suitable for integrating against any async Web framework, such as Starlette, Sanic, Responder, Quart, aiohttp, Tornado, or FastAPI.

  • Documentation: https://www.encode.io/databases/

  • Requirements: Python 3.7+

  • 下载: pip install databases

示例:在fastapi中使用databases操作MySQL(依赖aiomysql)

from fastapi import FastAPI
from databases import Database


app = FastAPI(title="使用databases")


async def databases_demo():
    # 实例化一个db连接并建立连接
    database = Database('mysql://root:12345@localhost:3306/db')
    await database.connect()

    # Run a database query.
    query = "SELECT * FROM users"
    rows = await database.fetch_all(query=query)
    return rows


@app.get("/")
async def index():
    return await databases_demo()

12-7 fastapi集成乌龟ORM

前面在第8章,我们给大家介绍了在同步代码中,想要使用ORM操作数据,使用了SQLAlchemy,

同样的在基于协程的异步代码中,操作数据库时也可以使用ORM,但此时就不能在使用SQLAlchemy(因为它不支持异步)

asyncio世界中,我们也有可以选择的ORM,比如: tortoise-orm 翻译过来就乌龟ORM。

乌龟ORM简介

  • 官网:https://tortoise-orm.readthedocs.io/en/latest/index.html

  • Tortoise ORM is an easy-to-use asyncio ORM (Object Relational Mapper) inspired by Django.

  • Tortoise ORM was build with relations in mind and admiration for the excellent and popular Django ORM.

  • Tortoise ORM is supported on CPython >= 3.7 for SQLite, MySQL and PostgreSQL.

  • 下载安装:pip3 install tortoise-orm

示例:fastapi简单集成乌龟ORM

# main.py

from fastapi import FastAPI
from tortoise import fields
from tortoise.models import Model
from tortoise.contrib.fastapi import register_tortoise


app = FastAPI(title="使用tortoise orm")


# 定义模型表
class User(Model):
    id = fields.IntField(pk=True)
    username = fields.CharField(max_length=255)
    password = fields.CharField(max_length=255)
    email = fields.CharField(max_length=255)

    class Meta:
        table = "users"		# 表示这个表对应数据库中的表名


# 使用register_tortoise 注册数据库信息
register_tortoise(
    app,
    db_url="mysql://root:12345@127.0.0.1:3306/db",
    modules={"models": ["main"]},	# 指定模型表所在的文本,"main" 表示mian.py中定义了User模型表
)


@app.get("/")
async def index():
    user = await User.filter(username="liuxu").first()
    user.email = "1111"
    await user.save()
	
    # 常用的CRUD方法
    # fake_user = await User.create(username="111", password="111", email="111")
    # await User.filter(id=fake_user.id).update(username="Updated name")
    # await User.filter(id=1).delete()

    return user

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

BoostCompass —— 搜索引擎

文章目录 一、项目简介二、Boost库简介1. 简介2. Boost 库的特点 三、项目主要模块1. 网页内容获取,数据预处理模块2. 建立正排索引和倒排索引,项目核心模块3. 编写 http_server 模块,进行网络开放 四、项目功能预览1. 项目文件预览2. 项目执…

基于储能电站服务的冷热电多微网系统 双层优化配置

目录 一、主要内容 二、部分代码 三、程序结果 四、下载链接 一、主要内容 随着储能技术的进步和共享经济的发展,共享储能电站服务模式将成为未来用户侧储能应用的新形态。提出基于储能电站服务的冷热电联供型多微网系统双层优化配置方法。 首先,提出…

【深入理解计算机系统第3版】有符号数和无符号数转换以及移位运算练习题2.23

题目 考虑下面的C函数&#xff1a; int fun1(unsigned word) {return (int) ((word << 24) >> 24); }int fun2(unsigned word) {return ((int) word << 24) >> 24; } 假设一个采用补码运算的机器上以32位程序来执行这些函数。还假设有符号数值的右移…

C易错注意之const修饰指针,含char类型计算,位段及相关经典易错例题

目录 前言 一&#xff1a;const修饰指针 1.const修饰变量 2.const 修饰指针 1.const int*p&m; 2. int* const p&m; 3. int const *p&m; 4. int const *const p&m; 5.总结 总之一句话为&#xff1a;左定值有定向 二&#xff1a;关于计算中char类型…

前端开发基础(HTML5 + CSS3)【第一篇】:HTML标签之文字排版、图片、链接、音频、视频 涵盖了两个综合案例 做到了基础学得会,实战写的出

点击前往前端开发基础专栏&#xff1a; 文章目录 HTML5 CSS3 开发一、开发环境搭建下载 VS Code1. 2 插件的下载1.3 项目和文件的下载 二、 什么是 HTML2.1 标签的语法2.2 代码演示&#xff1a;2.3 小结 三 、HTML基本骨架3.1 快捷键生成HTML骨架3.2 代码展示3.3 小结 四、标…

AI绘画:实例-利用Stable Diffusion ComfyUI实现多图连接:区域化提示词与条件设置

在Stable Diffusion ComfyUI中&#xff0c;有一种高级技巧可以让用户通过细致的区域化提示词来控制图像的不同部分&#xff0c;从而实现多图连接的效果。这种方法允许艺术家在同一画布上展现多个场景&#xff0c;创造出富有层次和故事性的图像。以下是实现这一效果的详细步骤。…

Transformer模型-Multi-Head Attention多头注意力的简明介绍

今天介绍transformer模型的Multi-Head Attention多头注意力。 原论文计算scaled dot-product attention和multi-head attention 实际整合到一起的流程为&#xff1a; 通过之前文章&#xff0c;假定我们已经理解了attention&#xff1b;今天我们按顺序来梳理一下整合之后的顺序。…

与机器对话:ChatGPT 和 AI 语言模型的奇妙故事

原文&#xff1a;Talking to Machines: The Fascinating Story of ChatGPT and AI Language Models 译者&#xff1a;飞龙 协议&#xff1a;CC BY-NC-SA 4.0 从 ELIZA 到 ChatGPT&#xff1a;会话式人工智能的简史 会话式人工智能是人工智能&#xff08;AI&#xff09;的一个分…

三子棋(C游戏)

文章目录 三子棋的描述思路关键代码运行代码 三子棋的描述 三子棋是一种民间传统游戏&#xff0c;又叫九宫棋、圈圈叉叉棋、一条龙、井字棋等。游戏分为双方对战&#xff0c;双方依次在9宫格棋盘上摆放棋子&#xff0c;率先将自己的三个棋子走成一条线就视为胜利&#xff0c;…

Flink运行机制相关概念介绍

Flink运行机制相关概念介绍 1. 流式计算和批处理2. 流式计算的状态与容错3. Flink简介及其在业务系统中的位置4. Flink模型5. Flink的架构6. Flink的重要概念7. Flink的状态、状态分区、状态缩放&#xff08;rescale&#xff09;和Key Group8. Flink数据交换9. 时间语义10. 水位…

【TSP旅行商问题】改进的大邻域搜索算法LNS

课题名称&#xff1a;基于改进的大规模邻域搜索算法LNS求解TSP问题 版本时间&#xff1a;2024-04-01 程序运行&#xff1a;直接运行LNS_TSP.m 文件即可 代码获取方式&#xff1a; QQ&#xff1a;491052175 VX&#xff1a;Matlab_Lover 模型介绍&#xff1a; 第一步&…

grep无法使用完整的正则表达式

问题描述 grep无法使用完整的正则表达式&#xff0c;比如前置断言、后置断言、\d和\t、\n等 问题原因 使用了扩展正则&#xff0c;而不是perl正则。规则和perl正则不同 从文档上讲得很清楚&#xff1a; -E PATTERN is an extended regular expression 他是扩展表达式&#…

ChatGPT 之联盟营销

原文&#xff1a;ChatGPT for Affiliate Marketing 译者&#xff1a;飞龙 协议&#xff1a;CC BY-NC-SA 4.0 第二章 制定转化对话 制定转化对话是每个营销人员和企业所有者都应该掌握的关键技能。它涉及创建和传递引人入胜的信息&#xff0c;吸引您的受众并激励他们采取行动。…

vue给input密码框设置眼睛睁开闭合对于密码显示与隐藏

<template><div class"login-container"><el-inputv-model"pwd":type"type"class"pwd-input"placeholder"请输入密码"><islot"suffix"class"icon-style":class"elIcon"…

spark-hive连接操作流程、踩坑及解决方法

文章目录 1 简介2 版本匹配3 spark hive支持版本源码编译3.1 spark-src下载3.2 maven换源3.3 spark编译 4 hive 安装与mysql-metastore配置4.1 mysql下载安装4.1.1 为mysql设置系统环境变量4.1.2 初次登陆更改root身份密码4.1.3 安装后直接更改密码 4.2 hive初始化4.2.1 编写hi…

Flutter仿Boss-4.短信验证码界面

效果 简述 在移动应用开发中&#xff0c;处理短信验证码是确保用户身份验证和安全性的重要步骤。本文将介绍如何使用Flutter构建一个短信验证码界面&#xff0c;让用户输入通过短信发送到他们手机的四位验证码。 依赖项 在这个项目中&#xff0c;我们将使用以下依赖项&#…

C# 实现子进程跟随主进程关闭

文章目录 前言一、如何实现&#xff1f;1、创建作业对象&#xff08;1&#xff09;、创建对象&#xff08;2&#xff09;、设置销毁作业时&#xff0c;关闭拥有的进程 2、子进程加入作业对象3、销毁作业对象&#xff08;1&#xff09;、手动销毁&#xff08;2&#xff09;、所在…

git 常用命令和使用方法

1.git理论基础 1.1git简介 git是一个开源的分布式版本控制系统&#xff0c;可以有效、高速地处理从很小到非常大的项目版本管理。 1.2git工作流程 在工作目录中&#xff0c;添加、修改文件将需要进行版本管理的文件放入暂存区中将暂存区域的文件提交到git仓库中 2.git基本…

SQLite的架构(十一)

返回&#xff1a;SQLite—系列文章目录 上一篇&#xff1a;SQLite下一代查询规划器(十&#xff09; 下一篇&#xff1a;SQLite—系列文章目录 介绍 本文档介绍SQLite库的架构。 这里的信息对那些想要了解或 修改SQLite的内部工作原理。 接口SQL 命令处理器虚拟机B-树…

ids工业相机与电控位移台同步控制及数据采集

通过VS2017和OpenCV,实现ids工业相机与电控位移台同步控制及数据采集 目录项目环境配置代码流程及思路项目架构项目开发运行效果开发关键ids相机配置位移台环境配置相机头文件相机参数设置保存图像函数设置电控位移台头文件电控位移台设置参数最后就是通过main函数进行调用和控…