最近抽时间做了个小项目是个抢座网站,同时用到了FastAPI作为后端,Celery来处理定时任务
核心部分是用户通过FastAPI的后端接口来提交任务到Redis,Celery每天定时从Redis里面获取任务信息然后运行内部的抢座脚本
踩坑的第一个点是,高版本的celery不支持windows!
踩坑的第二个点我为了图省事,开发的时候把Celery和我的抢座程序单独放在一起部署到了linux服务器上,然后我在本地用FastAPI调试接口去发布任务,导致服务器里的celery程序和本地fastapi程序里的代码不一致,celery报错不给我执行任务。
出问题的点就是:使用celery发布任务的后端程序代码,执行任务的celery程序里也要有一份
避坑指南就是:整个项目的代码我们一式两份,后端程序一份,celery程序一份!
下面我们配合docker来详细说一下这件事情
示例代码
假设我的项目主目录下有三个文件
-main.py #fastAPI后端
-script.py #耗时任务
-celery.py #celery
-requirements.txt
-DockerFile
-docker-compose.yml
script.py
#模拟一个耗时任务
import time
def get_seats(num):
time.sleep(5)
print(num)
return num
my_celery.py
#Celery
import celery
from script import get_seats
backend = 'redis://redis:6379/1'
broker = 'redis://redis:6379/0'
cel = celery.Celery('task',backend=backend,broker=broker)
@cel.task
def add_task(num):
result = get_seats(num)
return result
main.py
#fastAPI服务
import uvicorn
from fastapi import FastAPI
from my_celery import add_task
import datetime
app = FastAPI()
# 获取当前eta时间计算出10分钟后eta时间交给celery
#也就是任务提交10分钟后将会被定时执行
current_time = datetime.datetime.now()
set_time = current_time + datetime.timedelta(minutes=10)
@router.get('/add_task/{num}',summary='添加任务')
async def add_task(num:int):
data = add_task.apply_async(args=[num], eta=set_time)
print('- 任务已添加:', data.id)
return data.id
if __name__ == "__main__":
uvicorn.run('main:app', host='0.0.0.0', port=8000, reload=True, workers=1)
如何部署?
我们现在想用docker来部署这个程序,我们来想想我们需要多少个容器?
答案是3个,fastapi后端一个、celery一个、redis作为消息中间件一个
redis的部署就不用说了直接拉官方镜像就行啥也不用管
fastapi和celery怎么部署?我们需要自己写DockerFile来构建我们需要的python环境镜像
我们需要几个Dockerfile?答案是一个我们只需要一个python环境镜像,用同一个镜像来构建fastapi和clery项目就行
Dockerfile
FROM python:3.10.4
ENV PYTHONNUNBUFFERED = 1
WORKDIR /app
COPY requirements.txt requirements.txt
RUN pip3 install --upgrade pip
RUN pip3 config set global.index-url https://mirrors.aliyun.com/pypi/simple/
RUN pip3 config set install.trusted-host mirrors.aliyun.com
RUN pip3 install -r requirements.txt
COPY . /app
EXPOSE 8000
这里面最关键的点就是这一句
COPY . /app
我把所有的文件都copy到了容器的/app目录下
而我们fastapi容器和celery容器都是用这个镜像构建出来的,这就意味着,celery容器和fastapi容器里的所有文件都是一摸一样的,它们是同一份,这就保证了celery不会给我们报错
celery容器里有些文件可以不用但不能没有,所以我们直接全部的代码一式两份,两个容器一人一份,两个容器里的代码文件都一摸一样,不仅省事而且不会报错!
我们来看看docker-compose文件,你能清晰的看到这一点
version: '3.8'
services:
redis:
container_name: redis
image: redis:6.2-alpine
fastapi:
container_name: fastapi
build: . #都是从当前目录里去找DockerFile而目录里只有一份,所以用的是同一份
command: bash -c "python3 main.py"
volumes:
- .:/app
ports:
- "8000:8000"
depends_on:
- redis
celery:
container_name: celery
build: . #都是从当前目录里去找DockerFile而目录里只有一份,所以用的是同一份
command: bash -c "celery -A my_celery worker -l INFO"
volumes:
- .:/app
depends_on:
- redis
- backend
最后我们运行,就完事了
docker compose up
总结
上面的代码可能跑不起来,我写博客的时候也没去真实的测试,但是足够简洁足够说明问题
我们总结一下,这篇博客里面最重要的内容就是说明:我们部署celery程序和后端程序的时候,尽量要保证两者的代码是同一份,不然可能导致celery报错拒绝执行我们的任务!