构建高性能异步任务引擎:FastAPI + Celery + Redis

在现代应用开发中,异步任务处理是一个常见的需求。无论是数据处理、图像生成,还是复杂的计算任务,异步执行都能显著提升系统的响应速度和吞吐量。今天,我们将通过一个实际项目,探索如何使用 FastAPICeleryRedis 构建一个高性能的异步任务引擎。

项目背景

技术栈介绍

  • FastAPI:一个现代、高性能的 Web 框架,基于 Python 3.7+ 的异步编程特性构建。它支持自动生成 OpenAPI 文档和 Swagger UI,能够快速构建 RESTful API,并且具有极低的延迟和高并发处理能力。
  • Celery:一个分布式任务队列系统,主要用于处理异步任务和定时任务。它支持多种消息传输机制,能够将任务分发到多个工作节点上并行处理,从而提高系统的吞吐量和响应速度。
  • Redis:一个高性能的键值存储系统,常用于缓存、消息队列和分布式锁等场景。在 Celery 中,Redis 通常作为消息代理(Broker)和结果存储(Backend),负责任务的分发和结果的持久化。

项目目标

通过 FastAPI、Celery 和 Redis 的结合,构建一个能够高效处理用户提交的 Python 代码的异步任务引擎。用户可以通过 API 提交代码,系统会异步执行代码,并返回任务的执行结果。

项目目录结构

project/
├── main.py
├── utils.py
├── schemas.py
└── app/├── __init__.py├── config.py└── tasks/├── __init__.py└── tasks.py

代码功能深度解析

1. main.py:FastAPI 应用的核心

main.py 是项目的核心入口文件,负责定义 FastAPI 应用的接口和逻辑。

FastAPI 应用初始化
app = FastAPI(title="Async Task API", description="", version="1.0.0")

这里我们创建了一个 FastAPI 应用,命名为 Async Task API,版本为 1.0.0

自定义 Swagger UI
def swagger_monkey_patch(*args, **kwargs):return get_swagger_ui_html(*args,**kwargs,swagger_js_url="https://cdn.bootcdn.net/ajax/libs/swagger-ui/5.6.2/swagger-ui-bundle.js",swagger_css_url="https://cdn.bootcdn.net/ajax/libs/swagger-ui/5.6.2/swagger-ui.min.css",)
applications.get_swagger_ui_html = swagger_monkey_patch

通过 Monkey Patch 的方式,我们自定义了 Swagger UI 的资源加载路径,使用了国内的 CDN 加速资源,提升文档加载速度。

全局异常处理
@app.exception_handler(Exception)
def validation_exception_handler(request, err):base_error_message = f"Failed to execute: {request.method}: {request.url}"return JSONResponse(status_code=400, content={"message": f"{base_error_message}. Detail: {err}"})

我们定义了一个全局异常处理器,捕获所有未处理的异常,并返回一个包含错误信息的 JSON 响应。

HTTP 中间件:计算请求处理时间
@app.middleware("http")
async def add_process_time_header(request, call_next):start_time = time.time()response = await call_next(request)process_time = time.time() - start_timeresponse.headers["X-Process-Time"] = str(f'{process_time:0.4f} sec')return response

这个中间件用于计算每个请求的处理时间,并将处理时间添加到响应头 X-Process-Time 中,方便调试和性能优化。

创建任务的 API
@app.post('/tasks')
def create_pytask(task: schemas.PyTask):code = task.codetime_limit = task.time_limitexpires = task.expiresresult = execute_python_code.apply_async(args=(code,), time_limit=time_limit, expires=expires)return JSONResponse(content={"task_id": result.id})

用户可以通过 /tasks 接口提交 Python 代码,代码会被异步执行。任务的执行结果可以通过 /tasks/{task_id} 接口查询。

查询任务结果的 API
@app.get('/tasks/{task_id}', response_model=schemas.PyTaskResult)
def get_task_result(task_id: str):return get_task_info(task_id)

用户可以通过 /tasks/{task_id} 接口查询任务的执行结果和状态。

2. utils.py:任务信息获取工具

utils.py 文件定义了一个工具函数 get_task_info,用于获取 Celery 任务的状态和结果。

def get_task_info(task_id):task_result = AsyncResult(task_id, app=app)result = {"task_id": task_id,"task_status": task_result.status,"task_result": task_result.result}return result

通过 AsyncResult,我们可以获取任务的当前状态(如 PENDINGSUCCESSFAILURE 等)和执行结果。

3. schemas.py:数据模型定义

schemas.py 文件定义了 Pydantic 模型,用于验证和序列化请求和响应的数据。

任务请求模型
class PyTask(BaseModel):code: strexpires: Optional[int] = Nonetime_limit: Optional[int] = None

用户提交的任务请求包含以下字段:

  • code: 任务的 Python 代码。
  • expires: 任务的过期时间(可选)。
  • time_limit: 任务的时间限制(可选)。
任务结果模型
class PyProgramResult(BaseModel):status: stroutput: Optional[str] = Noneerror: Optional[str] = None

任务的执行结果包含以下字段:

  • status: 任务的执行状态(如 successfailure)。
  • output: 任务的标准输出(可选)。
  • error: 任务的错误输出(可选)。
任务结果响应模型
class PyTaskResult(BaseModel):task_id: strtask_status: strtask_result: Optional[PyProgramResult] = None

任务的查询结果包含以下字段:

  • task_id: 任务的 ID。
  • task_status: 任务的状态(如 PENDINGSUCCESS 等)。
  • task_result: 任务的执行结果(可选)。

4. app/__init__.py:Celery 应用初始化

app/__init__.py 文件是 Celery 应用的初始化文件,主要用于配置 Celery 应用和任务的自动发现。

创建 Celery 应用
app = Celery('my_celery_project')

我们创建了一个名为 my_celery_project 的 Celery 应用。

加载配置
app.config_from_object('app.config')

app.config 文件中加载 Celery 的配置。

自动发现任务
app.autodiscover_tasks(['app.tasks'])

自动发现 app.tasks 模块中的任务。

Worker 和 Beat 初始化
@worker_init.connect
def worker_initialization(**kwargs):print("Worker 初始化开始")@beat_init.connect
def beat_initialization(**kwargs):print("Beat 初始化开始")

定义了 Worker 和 Beat 的初始化函数,分别在 Worker 和 Beat 启动时执行。

5. app/config.py:Celery 配置

app/config.py 文件定义了 Celery 的配置。

消息代理和结果存储
broker_url = 'redis://:redisisthebest@redis:6379/0'
result_backend = 'redis://:redisisthebest@redis:6379/0'

使用 Redis 作为消息代理和结果存储。

任务结果过期时间
result_expires = 3600

任务结果在 Redis 中保存 1 小时后过期。

序列化配置
task_serializer = 'json'
result_serializer = 'json'
accept_content = ['json']

使用 JSON 作为任务和结果的序列化格式。

时区配置
timezone = 'Asia/Shanghai'
enable_utc = True

设置时区为 Asia/Shanghai,并启用 UTC 时间。

6. app/tasks/tasks.py:任务执行逻辑

app/tasks/tasks.py 文件定义了一个 Celery 任务 execute_python_code,用于执行用户提交的 Python 代码。

@app.task
def execute_python_code(code_string):temp_file = "temp_code.py"with open(temp_file, "w") as f:f.write(code_string)try:result = subprocess.run(["python3", temp_file],stdout=subprocess.PIPE,stderr=subprocess.PIPE,text=True)if result.stderr:return {"status": "failure", "error": result.stderr}else:return {"status": "success", "output": result.stdout}finally:if os.path.exists(temp_file):os.remove(temp_file)

该任务将用户提交的代码字符串保存为临时文件,然后使用 subprocess.run 执行该文件,捕获标准输出和错误输出。如果执行成功,返回 success 状态和标准输出;如果执行失败,返回 failure 状态和错误输出。最后,删除临时文件。

部署分析

version: '3.8'services:fastapi:image: lab:python-packagescontainer_name: fastapiports:- 8080:8080volumes:- D:\dockerMount\code\celery:/home/codeworking_dir: /home/codecommand: python3 main.pyrestart: unless-stoppednetworks:- mynetcelery-worker:image: lab:python-packagescontainer_name: celery-workervolumes:- D:\dockerMount\code\celery:/home/celeryworking_dir: /home/celerycommand: celery -A app worker --concurrency=4 --loglevel=inforestart: unless-stoppednetworks:- mynetcelery-flower:image: lab:python-packagescontainer_name: celery-flowerports:- 5555:5555volumes:- D:\dockerMount\code\celery:/home/celeryworking_dir: /home/celerycommand: celery -A app flower --port=5555restart: unless-stoppednetworks:- mynetredis:image: bitnami/redis:7.2.4-debian-12-r16container_name: redisenvironment:- REDIS_PASSWORD=redisisthebestnetworks:- mynetnetworks:mynet:external: false

在这个 Docker Compose 配置中,我们定义了三个服务:

  • fastapi:FastAPI 应用,负责接收用户请求并分发任务。
  • celery-worker:Celery 工作节点,负责执行异步任务。
  • celery-flower:Celery 的监控工具,提供任务执行的可视化界面。
  • redis:Redis 服务,作为 Celery 的消息代理和结果存储。

代码的功能和价值

功能

  1. 异步任务执行

    • 用户可以通过 /tasks 接口提交 Python 代码,代码会被异步执行。
    • 任务的执行结果可以通过 /tasks/{task_id} 接口查询。
  2. 任务状态管理

    • 任务的状态(如 PENDINGSUCCESSFAILURE 等)可以通过 /tasks/{task_id} 接口查询。
  3. 高性能和可扩展性

    • 使用 FastAPI 和 Celery 构建的异步任务引擎能够处理高并发的任务请求。
    • Celery 的分布式特性使得系统可以轻松扩展以应对更多的任务。
  4. 安全性

    • 通过设置 time_limitexpires,可以限制任务的执行时间和过期时间,防止恶意代码的长时间执行。
  5. 易用性

    • FastAPI 自动生成的 Swagger UI 使得 API 的使用和调试更加方便。
    • Pydantic 模型确保了请求和响应数据的类型安全。

价值

  1. 高效的任务处理

    • 该系统能够高效地处理大量异步任务,适用于需要异步执行代码的场景,如在线代码执行、数据处理、图像处理等。
  2. 可扩展性

    • 通过 Celery 的分布式任务队列,系统可以轻松扩展以处理更多的任务,适合高并发场景。
  3. 安全性

    • 通过限制任务的执行时间和过期时间,系统能够有效防止恶意代码的滥用。
  4. 易用性

    • FastAPI 和 Pydantic 的结合使得 API 的开发和维护更加简单,同时提供了自动生成的文档和类型检查。
  5. 灵活性

    • 系统支持自定义任务的执行逻辑,可以根据业务需求扩展任务类型和功能。

总结

通过 FastAPI、Celery 和 Redis 的结合,我们构建了一个高性能、可扩展的分布式异步任务引擎。它能够高效地处理用户提交的 Python 代码,并提供任务状态查询功能。该系统适用于需要异步执行代码的场景,具有高效、安全、易用和灵活的特点。

无论是构建一个在线代码执行平台,还是处理复杂的计算任务,这个项目都为你提供了一个强大的基础。希望这篇文章能为你带来启发,让你在异步任务处理的道路上走得更远!

附图

发送任务
在这里插入图片描述
查询结果
在这里插入图片描述

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

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

相关文章

介绍 Html 和 Html 5 的关系与区别

HTML(HyperText Markup Language)是构建网页的标准标记语言,而 HTML5 是 HTML 的最新版本,包含了一些新的功能、元素、API 和属性。HTML5 相对于早期版本的 HTML(比如 HTML4)有许多重要的改进和变化。以下是…

【win10+RAGFlow+Ollama】搭建本地大模型助手(教程+源码)

一、RAGFlow简介 RAGFlow是一个基于对文档深入理解的开源RAG(Retrieval-augmented Generation,检索增强生成)引擎。 主要作用: 让用户创建自有知识库,根据设定的参数对知识库中的文件进行切块处理,用户向大…

qwt 之 QwtPlotPicker

QwtPlotMarker 和 QwtPlotPicker 是 Qwt 库中用于增强 QwtPlot 功能的两个重要类。它们分别用于在图中添加标记和实现交互式的选择或拖动功能。 QwtPlotPicker 提供了交互式的选择工具,它允许用户通过鼠标点击或拖动来选择图表中的数据点或区域。这对于实现缩放、平…

C/C++圣诞树

系列文章 序号直达链接1C/C爱心代码2C/C跳动的爱心3C/C李峋同款跳动的爱心代码4C/C满屏飘字表白代码5C/C大雪纷飞代码6C/C烟花代码7C/C黑客帝国同款字母雨8C/C樱花树代码9C/C奥特曼代码10C/C精美圣诞树11C/C俄罗斯方块12C/C贪吃蛇13C/C孤单又灿烂的神-鬼怪14C/C闪烁的爱心15C…

lua dofile 传参数

cat 1.lua arg[1] 111 arg[2] 222 dofile(./2.lua) cat 2.lua print("First argument is: " .. arg[1]) print("Second argument is: " .. arg[2]) 执行 lua 1.lua,结果为: First argument is: 111 Second argument is: 222 l…

电商数据流通的未来:API接口的智能化与自动化趋势

在数字化时代,电子商务行业正在以前所未有的速度发展,而API(应用程序编程接口)接口作为电商领域的重要组成部分,其应用和发展趋势也日益受到关注。API接口作为电商系统与外部服务或平台交互的桥梁,对电商数…

投标心态:如何在“标海战术”中保持清醒的头脑?

在竞争激烈的市场环境下,“标海战术”——即大规模参与投标——已经成为许多企业争取市场份额的重要策略。然而,盲目追求投标数量可能导致资源浪费、团队疲劳以及战略目标的模糊化。在这种高强度的竞争模式中,如何保持清醒的头脑,…

【苍穹外卖】学习心得体会-随笔

前言 写了很久,终于可以完整运行项目了,记录下这几天的心得体会回顾一下知识点 第一天、Git 分布式版本控制工具 一、Git概述 定义:是分布式版本控制工具,用于管理软件开发中的源代码文件,像Java类、xml文件、html…

windows C#-使用构造函数

实例化类或结构时,将会调用其构造函数。 构造函数与该类或结构具有相同名称,并且通常初始化新对象的数据成员。 在下面的示例中,通过使用简单构造函数定义了一个名为 Taxi 的类。 然后使用 new 运算符对该类进行实例化。 在为新对象分配内存…

研发效能DevOps: Vite 使用 Element Plus

目录 一、实验 1.环境 2.初始化前端项目 3.安装 vue-route 4.安装 pinia 5.安装 axios 6.安装 Element Plus 7.gitee创建工程 8. 配置路由映射 9.Vite 使用 Element Plus 二、问题 1.README.md 文档推送到gitee未自动换行 2.访问login页面显示空白 3.表单输入账户…

5G 模组 RG500Q常用AT命令

5G 模组 RG500Q常用AT命令 5G 模组 RG500Q常用AT命令 at ATQNWPREFCFG\"mode_pref\",nr5g && sleep 1 at ATQNWPREFCFG\"nr5g_band\",79 && sleep 1 at atqnwlock\"commo…

NVIDIA DeepStream插件之Gst-nvtracker

NVIDIA DeepStream插件之Gst-nvtracker 1. 源由2. 基础知识3. Gst-nvtracker插件3.1 插件参数3.2 插件API接口 4. 分析问题5. 总结6. 参考资料 1. 源由 这篇的主要目的是稍微吐槽下NVIDIA的设计,当然其实他们做的还是不错的(从系统架构设计角度看&#…

进程内存转储工具|内存镜像提取-取证工具

1.内存转储,内存转储(Memory Dump)是将计算机的物理内存(RAM)内容复制到一个文件中的过程,这个文件通常被称为“内存转储文件”或“核心转储文件”(Core Dump),内存转储的主要目的是…

Lua语言入门 - Lua 面向对象

Lua 面向对象 面向对象编程(Object Oriented Programming,OOP)是一种非常流行的计算机编程架构,通过创建和操作对象来设计应用程序。 以下几种编程语言都支持面向对象编程: CJavaObjective-CSmalltalkC#Ruby Lua 是…

Pyqt6在lineEdit中输入文件名称并创建或删除JSON文件

1、创建JSON文件 代码 import osdef addModulekeyWordFile(self):if "" ! self.lineEdit_module.text():moduleFile self.lineEdit_module.text() .jsonelse:self.toolLogPrinting(请输入模块名称)returnfilePath modulekeyWordFileDir moduleFileif os.path.e…

【Leetcode 热题 100】236. 二叉树的最近公共祖先

问题背景 给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。 最近公共祖先的定义为:对于有根树 T T T 的两个节点 p p p、 q q q,最近公共祖先表示为一个节点 x x x,满足 x x x 是 p p p、 q q q 的祖先且 x x x 的深度尽可能大…

数据结构--堆的向上调整和向下调整

文章目录 1.完全二叉树2.堆向上调整3.堆向下调整4.测试代码 1.完全二叉树 下面的这个就是对于我们的完全二叉树的这个逻辑结构和物理结构的说明: 逻辑结构就是我们自己认为的进行购想出来的; 但是这个物理结构却是我们的这个数据结构在内存里面的真是…

智能挂号系统设计典范:SSM 结合 Vue 在医院的应用实现

摘要 随着信息技术在管理上越来越深入而广泛的应用,管理信息系统的实施在技术上已逐步成熟。本文介绍了医院预约挂号系统的开发全过程。通过分析医院预约挂号系统管理的不足,创建了一个计算机管理医院预约挂号系统的方案。文章介绍了医院预约挂号系统的系…

“魔法糖果盒的秘密:用朴素贝叶斯算法猜糖果颜色”

想象一下,你有一个神奇的糖果盒,这个糖果盒里有两种糖果:红色的和蓝色的。你闭上眼睛,从盒子里拿出一个糖果,然后尝一尝,你想知道这个糖果是红色的还是蓝色的。朴素贝叶斯算法就像是一个魔法规则&#xff0…

Transform组件的用法

文章目录 1. 概念介绍2. 使用方法3. 示例代码我们在上一章回中介绍了Checkbox Widget相关的内容,本章回中将介绍Transform Widget.闲话休提,让我们一起Talk Flutter吧。 1. 概念介绍 我们在这里说的Transform是一种容器类widget,它和Container组件类似。它可以包含其它的组件…