大家好,今天我想分享一下最近使用Celery + RabbitMQ与FastAPI的一些经历。最近我在一个与音频分析相关的项目上工作。在这个项目中,有些任务需要一点时间来完成。因此,当客户端向服务器发送请求时,服务器会暂时忙于处理这些任务。因此,响应客户端的请求会需要一些时间。我搜索了如何在后台运行任务来避免这种情况。最后,我发现了一个解决方案,Celery,一个分布式任务队列。在这篇文章中,我将描述我如何使用Celery来实现我的目标。
Celery 是一个带有实时处理的任务队列。它还支持任务调度。简单来说,当我的服务器收到一个耗时任务的请求时,我可以将该任务添加到任务队列中,在后台执行。Celery 会跟踪任务的状态,无论任务是等待、进行中、成功等。Celery 需要一个叫做 消息代理 的服务。当我的服务器(主应用程序)收到任务请求时,它需要在任务队列中等待,直到有可用的任务执行者。当有工作者可用时,它将从消息队列中取出任务并在后台执行。RabbitMQ 是 Celery 的默认消息代理。其他解决方案包括 Redis、Amazon SQS 和 Apache Kafka。我选择了 RabbitMQ,因为我想要尽可能简单地实现。这个动画视频将解释它是如何工作的。
Celery 是怎么工作的
实施首先,我们来设置RabbitMQ。我使用Windows环境开发我的项目。我使用Docker镜像来设置RabbitMQ。如果你想的话,当然也可以直接安装RabbitMQ,而不是用Docker。
运行一个Docker容器,将宿主机的15672和5672端口映射到容器内的相应端口,启动带有管理界面的RabbitMQ服务。
docker run -p 15672:15672 -p 5672:5672 rabbitmq:3-management
当Docker容器启动时,你可以通过浏览器在127.0.0.1:5672 访问并登录到RabbitMQ管理界面,用户名和密码都是guest。
RabbitMQ 仪表盘
现在我们来安装并配置Celery。安装下面这些库。我使用python-dotenv库来管理.env文件中的机密信息。
pip install celery python-dotenv
安装Celery和python-dotenv库。
我的 FastAPI 项目采用了类似的文件夹组织。
以下是一些主要文件和目录结构:
app/ # app主目录
│
├── config/ # 配置目录
│ ├── __init__.py # 初始化文件
│ └── celery_config.py # Celery配置文件
│
├── tasks/ # 任务目录
│ ├── __init__.py # 初始化文件
│ └── celery_tasks.py # Celery任务文件
│
├── __init__.py # 初始化文件
├── main.py # 主程序文件
├── .env # 环境变量配置文件
└── requirements.txt # 项目依赖列表
首先,在 .env
文件中创建我们的环境变量。Celery 需要两样东西。第一样是消息代理的 URL 地址。在我的例子中,这是 RabbitMQ 服务器的 URL。(请将 <USERNAME>
和 <PASSWORD>
替换为您的用户名和密码,默认用户是 guest
。)第二样是结果后端。这里 Celery 存储任务执行的结果。完成任务后,我们就可以查看结果。
CELERY_BROKER_URL=amqp://<USERNAME>:<PASSWORD>@localhost:5672// # 设置Celery使用的AMQP代理
CELERY_RESULT_BACKEND=rpc:// # 指定Celery的结果后端
好的。行了,我们现在来创建我们的 Celery 应用程序,然后配置一下它。我为此创建了一个叫 celery_config.py 的文件。
# celery_config.py
# 用于定义 Celery 任务的配置文件。
import os
from celery import Celery
from dotenv import load_dotenv
load_dotenv() # 加载环境变量以获取配置信息
celery_app = Celery(__name__, broker=os.getenv("CELERY_BROKER_URL"), backend=os.getenv("CELERY_RESULT_BACKEND"))
celery_app.conf.update(
imports=['app.tasks.celery_tasks'], # 指向您的 Celery 任务文件的路径
broker_connection_retry_on_startup=True, # 启动时重试 broker 连接
task_track_started=True
)
我创建了一个单独的文件来定义 Celery 任务,但这并不是必要的。你可以直接在这个文件里定义你的任务。如果你这样做了,就无需再添加 imports=['app.tasks.celery_tasks'] 这行了。这样配置就完成了。
# celery_config.py
# 此文件用于配置 celery 任务定义。
import os
from celery import Celery
from dotenv import load_dotenv
load_dotenv() # 加载环境变量
celery_app = Celery(__name__, broker=os.getenv("CELERY_BROKER_URL"), backend=os.getenv("CELERY_RESULT_BACKEND"))
celery_app.conf.update(
broker_connection_retry_on_startup=True, # 启动时 broker 连接重试 = 真,
task_track_started=True # 任务开始跟踪 = 真
)
现在,我们来创建一些celery任务。我创建了一个celery_tasks.py文件,并在里面定义了任务。我们使用装饰器来定义这些celery任务。
# celery_tasks.py
import asyncio
from app.config.celery_config import celery_app
@celery_app.task
def my_task(x, y):
ans = x + y
print(ans)
return ans
async def my_async_task(x, y):
await asyncio.sleep(3)
ans = x + y
print(ans)
return ans
@celery_app.task
def my_second_task(x, y):
result = asyncio.run(my_async_task(x, y))
return result
这样你可以在配置文件中定义任务,而不是使用单独的文件。
# celery_config.py
# 在此文件中配置celery任务。
import os
import asyncio
from celery import Celery
from dotenv import load_dotenv
load_dotenv() # 加载环境变量
celery_app = Celery(__name__, broker=os.getenv("CELERY_BROKER_URL"), backend=os.getenv("CELERY_RESULT_BACKEND"))
celery_app.conf.update(
broker_connection_retry_on_startup=True,
task_track_started=True # 从任务开始追踪
)
@celery_app.task
def my_task(x, y):
ans = x + y
print(ans)
return ans
async def my_async_task(x, y):
await asyncio.sleep(3)
ans = x + y
print(ans)
return ans
@celery_app.task
def my_second_task(x, y):
result = asyncio.run(my_async_task(x, y))
return result
现在,我们可以使用来自路由处理器的任务。这是一个示例用的路由处理器。在这里,我创建了一个 GET 端点。在这个端点处理程序中,我使用了 Celery 任务。
从 fastapi 导入 FastAPI 作为 FastAPI
从 app.tasks.celery_tasks 导入 my_task 作为 my_task
app = FastAPI()
@app.get("/run")
def handle_run():
task_response = my_task.delay(5, 6)
return {"message": "任务执行已开始"}
如果 __name__ == '__main__':
uvicorn.run(app, port=8080)
好的。我们的编码部分结束了。现在,让我们运行我们的 Celery 工作者。这个 Celery 工作者将会执行 RabbitMQ 消息队列中的任务。如果你没有启动 RabbitMQ 服务器的话,在启动 Celery 工作者之前,请先启动 RabbitMQ 服务。
celery --app app.config.celery_config.celery_app worker --loglevel=info --pool=solo
如果 celery 顺利运行,你可以在终端看到这样的输出。
香菜的命令行输出
在上面的图中,在 [任务] 部位,我们可以看到 celery 在 celery_tasks.py 文件中定义的任务。当任务队列接收到新任务后,celery 就会执行任务。现在我们来启动 FastAPI 服务器。
uvicorn app.main:app --port 8000
运行上述命令以启动应用程序。
在 Swagger 文档页上,我们来试一下这个端点。发出请求后,如果检查 celery 终端,可以看到类似这样的结果。
它显示任务已接收并开始执行。执行完成后,它显示任务成功。这意味着我们的任务并不是由FastAPI服务器来执行的。我们所有的任务都是由celery工作者来完成的。
任务监控我们可以使用花来监控celery任务及其工作者,现在我们来安装它。
运行以下命令来安装flower:
pip install flower
接着在你的本地电脑上运行flower程序
celery flower --app app.config.celery_config.celery_app --broker:amqp://localhost//
使用 celery flower
命令启动 celery 花瓣监控工具,其中 --app
参数指定了 celery 应用的配置路径,而 --broker
参数指定了消息代理的地址。
你可以在网页浏览器中查看花卉监控工具。访问http://localhost:5555/。
Flower
这是我如何在我的FastAPI项目中使用Celery和RabbitMQ的方法。我想你已经理解了这里的情况。这是最简单的方法之一。如果你想的话,你可以创建更多的任务队列。还有很多可以调整的设置。你可以查看他们的文档。你可以利用WebSocket来通知客户端任务是否完成。就这样吧。下次博客见喽。祝你编程愉快!
栈学社 🎓感谢您一直读到最后,临走前还想说几句:
- 请给她鼓掌并关注她!👏
- 关注我们 X | LinkedIn | YouTube | Discord
- 访问我们的其他平台: In Plain English | CoFeed | Differ
- 更多内容请访问我们网站 Stackademic.com
共同学习,写下你的评论
评论加载中...
作者其他优质文章