FastAPI性能对比:同步vs异步

大家好,FastAPI已成为构建Python API的最流行框架之一,因其速度和易用性而广受欢迎。但在构建高性能应用程序时,使用同步(sync)还是异步(async)代码执行是很重要的问题。本文将通过现实世界的性能测试,对同步和异步的FastAPI实现进行基准测试,并深入分析相关数据,以帮助大家决定何时使用这两种方法。

1.同步与异步在FastAPI中的区别

  • 同步代码(sync):在同步执行中,任务一个接一个地处理。每个请求都需要等待前一个请求完成,这在用户数量较多或存在慢I/O操作(如数据库查询或文件上传)时,可能会导致瓶颈。

  • 异步代码(async):异步执行支持多个请求并发处理。应用程序无需等待I/O操作(如数据库调用)完成,而是可以继续处理其他请求,从而在高并发环境中更有效率。

2.设置:在FastAPI中基准测试同步与异步

为了比较同步和异步实现,在这里创建了两个版本的FastAPI应用程序。

同步版本:使用传统的阻塞数据库查询,采用psycopg2

异步版本:使用非阻塞的异步查询,采用asyncpg

这两个版本都执行一个简单的任务:从PostgreSQL数据库查询用户。数据库中包含极少的数据,以便隔离同步/异步机制的影响。

技术栈:

  • 使用FastAPI作为API框架。

  • 使用SQLAlchemy作为ORM。

  • 使用psycopg2或psycopg2-binary(同步)和asyncpg(异步)进行数据库连接。

  • 使用PostgreSQL作为数据库。

为了测试性能,使用**Apache Benchmark(ab)**模拟了1000个请求,具有100个并发连接。

2.1 同步版本代码

在同步版本中,在这里使用psycopg2驱动与SQLAlchemy,后者可执行阻塞查询。表格是使用同步的SQLAlchemy引擎创建的。

同步:main.py

from fastapi import FastAPI, Depends, HTTPException
from sqlalchemy.orm import Session
from .database import get_db, Userapp = FastAPI()@app.get("/users/{user_id}")
def get_user(user_id: int, db: Session = Depends(get_db)):# 同步和阻塞user = db.query(User).filter(User.c.id == user_id).first()if not user:raise HTTPException(status_code=404, detail="User not found")return {"id": user.id, "name": user.name, "email": user.email}

同步:database.py

from sqlalchemy import create_engine, MetaData, Table, Column, Integer, String
from sqlalchemy.orm import sessionmakerDATABASE_URL = "postgresql://user:password@localhost/db_name"# 创建同步的SQLAlchemy引擎
engine = create_engine(DATABASE_URL, echo=True)# 创建用于同步查询的会话生成器
SessionLocal = sessionmaker(bind=engine, autoflush=False, autocommit=False)# 定义元数据
metadata = MetaData()# 定义User表
User = Table("users", metadata,Column("id", Integer, primary_key=True),Column("name", String),Column("email", String),
)# 在数据库中创建表
metadata.create_all(engine)# 获取同步数据库会话的依赖
def get_db():db = SessionLocal()try:yield dbfinally:db.close()

2.2 异步版本代码

在异步版本中,在这里使用asyncpg驱动与SQLAlchemy,进行非阻塞查询。然而,表的创建仍然是同步进行的,因为SQLAlchemy的metadata.create_all()不支持异步。

异步:main.py

from contextlib import asynccontextmanager
from fastapi import FastAPI, Depends, HTTPException
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select
from .database import get_async_db, User, initialize_database@asynccontextmanager
async def lifespan(app: FastAPI):# 启动:初始化数据库await initialize_database()yieldapp = FastAPI(lifespan=lifespan)@app.get("/users/{user_id}")
async def get_user(user_id: int, db: AsyncSession = Depends(get_async_db)):result = await db.execute(select(User).where(User.c.id == user_id))user = result.fetchone()if not user:raise HTTPException(status_code=404, detail="User not found")return {"id": user.id, "name": user.name, "email": user.email}

异步:database.py

from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy import MetaData, Table, Column, Integer, StringDATABASE_URL = "postgresql+asyncpg://user:password@localhost/db_name"# 用于异步查询的异步引擎
engine = create_async_engine(DATABASE_URL,echo=True,pool_size=10,max_overflow=20,
)# 用于异步查询的异步会话
AsyncSessionLocal = sessionmaker(bind=engine, class_=AsyncSession, expire_on_commit=False
)# 定义元数据
metadata = MetaData()# 定义User表
users = Table("users",metadata,Column("id", Integer, primary_key=True),Column("name", String),Column("email", String),
)# 创建所有表
async def init_db():async with engine.begin() as conn:await conn.run_sync(metadata.create_all)# 获取异步数据库会话的依赖
async def get_async_db():async with AsyncSessionLocal() as session:yield session# 启动:初始化数据库
async def initialize_database():await init_db()

在这个版本中,请求是以异步方式处理的,支持多个请求在等待I/O时并发处理。

3.基准测试命令

同步版本:ab -n 1000 -c 100 http://127.0.0.1:8000/users/1

异步版本:ab -n 1000 -c 100 http://127.0.0.1:8001/users/1

以下是基准测试的性能指标分析:

图片

同步vs异步(Airtable)的基准测试结果如下。

每秒请求数:异步版本每秒可处理50.68个请求,而同步版本每秒只能处理36.89个请求。在相同时间内,异步处理的请求数比同步版本多37%,因此在并发性方面,异步显然胜出。

每个请求的响应时间(平均值):异步版本的平均响应时间低于同步版本27%(1973毫秒vs2710毫秒),这表明在高负载情况下,异步处理请求的效率更高。

最长请求时间:两个版本的最长请求时间相似(约4000毫秒),但异步版本的表现更稳定,这体现在响应时间的波动较小。

图片

以上是同步和异步版本在不同百分位数下的比较图,包括平均和最长请求时间。

  • 实线表示不同百分位数下的响应时间。

  • 虚线表示同步(2710.648毫秒)和异步(1973.057毫秒)的平均响应时间。

  • 点线突出显示同步(4167毫秒)和异步(3851毫秒)的最长请求时间。

4.FastAPI中同步与异步的选择

使用异步的情况:

  • 应用程序需要处理高流量和大量并发用户。

  • 应用程序是与I/O绑定的,需要进行大量数据库查询或API调用。

  • 需要为大量请求最小化响应时间。

使用同步的情况:

  • 应用程序的并发量较小,或主要执行CPU密集型任务。

  • 希望保持代码库的简单性,避免异步处理的复杂性。

  • 不希望应用程序扩展到同时处理数百或数千个请求。

5.优化异步性能

虽然异步在这些测试中速度更快,但仍有一些方法可以进一步优化。

  • 连接池:使用连接池重用数据库连接,避免为每个请求创建一个新连接。

  • 使用异步库:确保所有I/O绑定的任务(例如文件读/写、外部API调用)都以异步方式处理,以获得最佳性能。

  • 测试更高的并发性:进行更高并发量的负载测试(例如,500+用户),以充分发挥异步的优势。

engine = create_async_engine(DATABASE_URL,pool_size=10,max_overflow=20
)

如果应用程序需要处理大量并发用户,并严重依赖于I/O绑定任务,那么异步FastAPI可以提供更好的性能、可扩展性和响应能力。不过对于更简单的用例,可以选择同步实现。

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

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

相关文章

diffusion model 学习笔记

条件引导的 diffusion 对于无条件的DDPM 而言 p ( x t ∣ x 0 ) ∼ N ( α t ˉ x 0 , 1 − α t ˉ ⋅ I ) p(x_t | x_0) \sim \mathcal{N}( \sqrt{\bar{\alpha_t}} x_0, 1-\bar{\alpha_t} \cdot \mathrm{I} ) p(xt​∣x0​)∼N(αt​ˉ​ ​x0​,1−αt​ˉ​⋅I) 可以得到…

3.4 大数据生态

文章目录 1. 数据存储1.1 Apache Hadoop - HDFS1.2 Apache HBase1.3 Apache Kudu1.4 云平台存储组件 2. 数据计算2.1 Apache Hadoop - MapReduce2.2 Apache Hive2.3 Apache Spark2.4 Apache Flink 3. 数据传输3.1 Apache Kafka3.2 Apache Pulsar3.3 Apache Flume3.4 Apache Sqo…

Community Enterprise Operating System

起源与背景 CentOS项目始于2003年,由一群热心的Linux用户和开发者共同发起。 它的诞生旨在为用户提供一个免费且与RHEL高度兼容的操作系统,满足那些希望使用RHEL的稳定性和安全性但又不想支付商业许可费用的用户和组织的需求。 CentOS社区会将Red Hat…

IT运维管理工具大全

IT运维管理工具大全 IT运维管理工具是IT运维团队的重要工具之一,使用它们可以提高工作效率、减少人为错误、自动化任务、提高IT基础设施和应用程序的可靠性和安全性等。以下是更详细的原因: 提高工作效率: 使用IT运维管理工具可以自动化许多…

论文精读:PRL RuO2中不存在交错磁自旋分裂

Phys. Rev. Lett., 2024, 133, 176401. https://doi.org/10.1103/PhysRevLett.133.176401 https://mp.weixin.qq.com/s/Miv6kvT5vh-Sha1xP38YLQ 摘要节选 金红石RuO2被认为是一种潜在的d波交变磁候选材料,预测自旋分裂高达1.4 eV。尽管积累了理论预测和输运测量…

【GIN】go-gin 中 validator 验证功能

文章目录 前言一、基础用法二、常用字段说明常用字段说明1. required2. len3. min 和 max4. gte 和 lte 、 gt 和 lt 、ne5. oneof6. email7. url 三、示例代码运行效果 总结 前言 在 Go 中使用 Gin 框架时,BindJSON 可以将 JSON 请求体中的数据绑定到结构体上&…

[ 问题解决篇 ] win11中本地组策略编辑器gpedit.msc打不开(gpedit.msc缺失)

🍬 博主介绍 👨‍🎓 博主介绍:大家好,我是 _PowerShell ,很高兴认识大家~ ✨主攻领域:【渗透领域】【数据通信】 【通讯安全】 【web安全】【面试分析】 🎉点赞➕评论➕收藏 养成习…

RSTP的工作过程

RSTP简介: 生成树协议(STP)用于在网络中防止环路产生,但 STP 的收敛速度较慢。 RSTP(Rapid Spanning Tree Protocol )快速生成树协议:RSTP 是对 STP 的改进,它能在网络拓扑发生变化…

HTML 结构化标签完全指南:<html>、<head>、<body> 和布局标签 <div>、<span> 的功能及其在网页中的应用

文章目录 1. <html> 标签2. <head> 标签3. <body> 标签4. <div> 标签5. <span> 标签小结 在 HTML 文档中&#xff0c;使用特定的结构标签可以有效地组织和管理网页内容。这些标签不仅有助于浏览器正确解析和渲染页面&#xff0c;还能提高网页的可…

Vue3 生命周期 - 2024最新版前端秋招面试短期突击面试题【100道】

Vue3 生命周期 - 2024最新版前端秋招面试短期突击面试题【100道】 &#x1f504; 在Vue.js中&#xff0c;生命周期钩子是组件从创建到销毁过程中的一系列事件。理解Vue的生命周期对于掌握组件的行为和调试至关重要。Vue 3引入了Composition API&#xff0c;改变了生命周期函数…

刘艳兵-DBA021-升级到Oracle Database 12c时,关于使用Export/Import方法迁移数据的说法是正确的?

升级到Oracle Database 12c时&#xff0c;关于使用Export/Import方法迁移数据的说法是正确的&#xff1f; A 仅当源数据库在只读模式下没有任何表空间时&#xff0c;才可以使用它来迁移数据库。 B 仅当源数据库和目标数据库字节序相同时&#xff0c;才可以使用它来迁移数…

php把十六进制转化成字符串 和 字符串转十六进制

最近项目中碰到需要把接收十六进制的数据&#xff0c;十六进制的数据不便阅读 方法一&#xff0c;只是不同的函数 // 十六进制转字符串 function hexToStr($hex) {$hex str_replace( , , $hex); // 去除空格$string ;for ($i 0; $i < strlen($hex) - 1; $i 2) {$st…

异步编程的利之Future模式深入解析(In Depth Analysis of Future Patterns)

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 本人主要分享计算机核心技…

Q_GLOBAL_STATIC使用

作用&#xff1a;线程安全的全局静态变量初始化 声明&#xff1a; Q_GLOBAL_STATIC(MyType,globalState) Q_GLOBAL_STATIC_WITH_ARGS(MyType, globalState, (42, "Hello", "World")) //带参数的初始化 注&#xff1a; 构造函数和析构函数必须是公有的 如果…

《TCP/IP网络编程》学习笔记 | Chapter 1:理解网络编程和套接字

《TCP/IP网络编程》学习笔记 | Chapter 1&#xff1a;理解网络编程和套接字 《TCP/IP网络编程》学习笔记 | Chapter 1&#xff1a;理解网络编程和套接字基本概念服务端客户端 基于 Linux 平台的 "Hello world!" 服务端和客户端基于 Linux 的文件操作打开文件关闭文件…

代理人工智能如何应对现代威胁的速度和数量

Seven AI首席执行官 Lior Div 讨论了代理 AI 的概念及其在网络安全中的应用。他解释了代理 AI 与传统自动化安全系统的区别&#xff0c;即代理 AI 具有更大的自主性和决策能力。 Div 强调&#xff0c;通过实时处理大量警报&#xff0c;代理 AI 特别适合对抗现代 AI 驱动的威胁…

Supabase:当开源遇上实时数据库服务

在当代应用开发的浪潮中,我们见证了无服务器架构和实时数据库服务的崛起。Firebase 的成功验证了这一方向,但同时也带来了供应商锁定的困扰。正是在这样的背景下,Supabase 横空出世,以开源的姿态为开发者提供了一个全新的选择。 为什么 Supabase 值得关注&#xff1f; 当我们…

【华为HCIP实战课程二十九】中间到中间系统协议IS-IS邻居关系建立和LSP详解,网络工程师

一、广播环境邻居关系建立详解 1、广播环境邻居关系建立 广播邻居关系采用三次握手,携带的邻居列表为接口的MAC来标识 2、LSP同步:3种报文(CSNP和PSNP和具体的LSP) CSNP作用类似DBD,请求者发送PSNP(类似LSR)来请求具体的LSP 广播网络LSP交互过程: R1-R2(DIS)--R3…

<HarmonyOS第一课>应用/元服务上架的课后习题

善者&#xff0c;吾善之&#xff1b; 不善者&#xff0c;吾亦善之&#xff0c;德善。 信者&#xff0c;吾信之&#xff1b; 不信者&#xff0c;吾亦信之&#xff0c;德信。 圣人在天下&#xff0c;歙歙焉为天下浑其心&#xff0c;百姓皆注其耳目&#xff0c;圣人皆孩之。 通过&…

游戏引擎中Static,Kinematic,Dynamic三种刚体属性

一.刚体属性 为了提高物理检测效率和内存使用,引擎只对特殊标识的刚体属性进行检测。包括常用的RayCast检测,BoxCast检测,AABB包围盒检测。 Static:实际游戏中不可能发生移动的房子,树木,建筑物等。引擎初始化进行Collider刷新 Kinematic:实际游戏中奇特的物体,传送门,陷阱等, …