Fastapi的服务端后台任务简介

前言

回顾一下客户端发送网络请求的过程。

同步请求

如常见的requests发起的请求。

python 复制代码
import requests

def send_sync_request(url, data):
    response = requests.post(url, data=data)
    return response.text

def main():
    url = 'https://example.com/api/endpoint'
    data = {'key': 'value'}

    response = send_sync_request(url, data)
    print(response)

# 运行主函数
main()

如果需要同时发送多个,可以采用多线程并发。

python 复制代码
import requests
import threading 

def send_sync_request(url, data):
    response = requests.post(url, data=data)
    return response.text

def main():
    url = 'https://example.com/api/endpoint'
    data = {'key': 'value'}

	threads = []
	# 并发2个请求
	nums = 2
	for i in range(nums):
	    t = threading.Thread(target=send_sync_request, args=(url, data))
	    threads.append(t)
	
	for i in range(nums):
	    threads[i].start()
	
	for i in range(nums):
	    threads[i].join()
	    
	print("结束")
	
# 运行主函数
main()

异步请求

requests 不支持异步请求,因此可以采用aiohttp来做。

python 复制代码
import asyncio
import aiohttp

async def make_async_request(url, data):
    async with aiohttp.ClientSession() as session:
        async with session.post(url=url, data=data) as response:
            return  await response.read()

async def main():
    url = 'https://example.com/api/endpoint'
    data = {'key': 'value'}
	response = await make_async_request(url, data)
    print(response)

# 运行主函数
await main()

并发多个请求,可以直接用asyncio.gather()发起。

python 复制代码
import asyncio
import aiohttp

async def make_async_request(url, data):
    async with aiohttp.ClientSession() as session:
        async with session.post(url=url, data=data) as response:
            return  await response.read()

async def main():
    url = 'https://example.com/api/endpoint'
    data = {'key': 'value'}

    # 并发2个
	await asyncio.gather(
        make_async_request(url, data),
        make_async_request(url, data)
    )

# 运行主函数
await main()

服务端应用响应

同步与异步请求

通过fastapi定义一个简单的应用,获取计算结果后返回给客户端。

python 复制代码
from fastapi import FastAPI

app = FastAPI()

@app.post("/sync_endpoint")
def sync_endpoint():
    # 执行同步操作
    result = do_sync_task()
    # 返回同步操作的结果
    return {"result": result}

def do_sync_task():
    # 模拟同步操作等待两秒钟
    time.sleep(2)
    return "Sync Task Completed"

如果处理时间过长,比如需要等待IO,则可以改为异步任务,在中间穿插其他任务的执行,以此提高服务端的响应能力。

python 复制代码
from fastapi import FastAPI

app = FastAPI()

@app.post("/async_endpoint")
async def async_endpoint():
    # 执行异步操作
    result =  do_async_task()
    # 夹杂其他操作
    # ...
    # 等待完成
    await result
    # 返回异步操作的结果
    return {"result": result}

async def do_async_task():
    # 模拟异步操作,比如发送异步请求、访问数据库等
    await asyncio.sleep(2)  # 模拟等待两秒钟
    return "Async Task Completed"

后台任务

以上的服务,不管是异步还是同步,服务端都会占用响应,直到返回结果前都不会结束。而在 fastapi 中,可以使用后台任务(Background Tasks)来异步执行一些耗时的操作,而无需等待其完成。后台任务非常适用于需要进行一些异步处理的场景,例如发送电子邮件、处理图像、推送通知等。 要使用后台任务,需要导入 BackgroundTasks 类,并在路由处理函数中将其作为一个参数传递。然后,可以使用 background 对象的 add_task() 方法来添加后台任务。

python 复制代码
from fastapi import BackgroundTasks, FastAPI
import time
app = FastAPI()


def write_notification(email: str, message=""):
    with open("log.txt", mode="w") as email_file:
        content = f"notification for {email}: {message}"
        time.sleep(3)
        email_file.write(content)


@app.get("/send-notification")
async def send_notification(email: str, background_tasks: BackgroundTasks):
    background_tasks.add_task(write_notification, email, message="some notification")
    return {"message": "Notification sent in the background"}
    
   

在 FastAPI 中使用后台任务来处理一些耗时的操作可以提高应用程序的响应性能。但是,后台任务不适合需要返回结果给客户端的场景,因为客户端无法立即获取到后台任务的结果。

如果一定要用后台任务来处理需要返回结果的任务,也是可以完成的,比如添加一些ID信息用于后续程序获取原任务结果。一个简单的例子如下。

python 复制代码
result = {}


@app.get("start_task")
def start_task(params, background_tasks: BackgroundTasks):
    def bg_task(task_id, params):
        result[task_id] = func(params) # 某个耗时的函数


    task_id = uuid.uuid4().hex
    background_tasks.add_task(bg_task, task_id, params)
    return {"submit task id is":task_id}



@app.get("/get_result")
def get_result(task_id):
    if task_id in result:
        return {"result": result[task_id]}
    return {"result":"task not finish"}

在上面的例子,客户端发送请求后,会马上得到"submit task id is:xxx"这个结果,但耗时函数的结果并没有同步返回。当需要耗时函数的结果时,可以通过发送另一个get 请求get_result,在此之前可以做一些其他的操作,从而提高客户端的程序响应能力。

相关推荐
星就前端叭41 分钟前
【开源】一款基于Vue3 + WebRTC + Node + SRS + FFmpeg搭建的直播间项目
前端·后端·开源·webrtc
9527华安1 小时前
FPGA多路MIPI转FPD-Link视频缩放拼接显示,基于IMX327+FPD953架构,提供2套工程源码和技术支持
fpga开发·架构·音视频
小林coding2 小时前
阿里云 Java 后端一面,什么难度?
java·后端·mysql·spring·阿里云
AI理性派思考者2 小时前
【保姆教程】手把手教你在Linux系统搭建早期alpha项目cysic的验证者&证明者
后端·github·gpu
从善若水2 小时前
【2024】Merry Christmas!一起用Rust绘制一颗圣诞树吧
开发语言·后端·rust
机器之心2 小时前
终于等来能塞进手机的文生图模型!十分之一体量,SnapGen实现百分百的效果
人工智能·后端
机器之心2 小时前
首次!大模型自动搜索人工生命,做出AI科学家的Sakana AI又放大招
人工智能·后端
运维&陈同学3 小时前
【模块一】kubernetes容器编排进阶实战之基于velero及minio实现etcd数据备份与恢复
数据库·后端·云原生·容器·kubernetes·etcd·minio·velero
waterme1onY3 小时前
Spring AOP 中记录日志
java·开发语言·笔记·后端
全栈开发帅帅4 小时前
基于springboot+vue实现的博物馆游客预约系统 (源码+L文+ppt)4-127
java·spring boot·后端