独立容器运行Celery遇任务未注册错误求助
问题现象
在独立容器中运行Celery处理Flask邮件任务时,Redis消息队列已收到任务消息并转发给Celery,但Celery抛出错误:
Received unregistered task of type 'flask_project.views.send_async_email'. The message has been ignored and discarded.
已配置Celery的include参数,且重启、重建容器后问题仍未解决。
相关代码与配置
Flask视图代码
from flask import Blueprint, current_app from flask_mail import Mail from os import getenv from celery import Celery from .support_func import decorator_require_api views = Blueprint('views', __name__) celery = Celery(views.name, broker='redis://redis:6379/0', include=["views.tasks"]) @celery_task def send_async_email(email_data): mail = Mail() mail.send(email_data) @views.route('/') def home(): with current_app.app_context(): email_data = {'sender': getenv('MAIL_USERNAME'), 'recipients': ['mrjoli021@gmail.com'], 'message': "This is a test email"} send_async_email.delay(email_data) return "Message sent!"
Docker Compose配置
--- version: "3.9" services: flask: build: context: ./Docker/flask container_name: flask volumes: - ./app/:/app restart: unless-stopped stdin_open: true networks: - api nginx: image: nginx:latest container_name: nginx depends_on: - flask volumes: - ./nginx_config:/etc/nginx/conf.d - ./app/:/app ports: - "5000:443" networks: - api celery: build: context: ./Docker/celery container_name: celery depends_on: - redis restart: unless-stopped stdin_open: true networks: - api redis: image: redis:latest container_name: redis depends_on: - flask networks: - api networks: api: driver: bridge
Celery容器Dockerfile
FROM python:3.9.7-slim-buster WORKDIR /app RUN apt-get update && apt-get install -y \ build-essential # python-dev libssl-dev openssl COPY ./ . RUN pip3 install -r requirements.txt ENV CELERY_BROKER_URL=redis://redis:6379/0 CMD ["celery", "worker", "--loglevel=info"]
问题根源与解决方法
1. 任务装饰器使用错误
代码里用了@celery_task装饰任务,但正确的Celery任务装饰器应该是@celery.task(注意是点号而非下划线),这个错误会导致任务无法被Celery实例正确注册。
2. include路径不匹配
当前Celery配置的include=["views.tasks"],但任务send_async_email直接定义在views蓝图文件中,并非views.tasks模块下。需要调整include为任务所在的正确模块路径:
celery = Celery(views.name, broker='redis://redis:6379/0', include=["flask_project.views"])
(要使用完整的Python模块路径,即从项目根目录开始的导入路径)
3. Celery启动参数缺失
Celery Worker启动时必须指定应用的Celery实例位置,否则无法加载任务定义。修改Celery容器的CMD:
CMD ["celery", "-A", "flask_project.views.celery", "worker", "--loglevel=info"]
这里flask_project.views.celery是Celery实例的完整导入路径,确保Worker能找到并加载任务。
4. 容器代码不同步
Celery容器的构建上下文是./Docker/celery,需确保该容器能访问到完整的Flask项目代码。可以在Docker Compose的Celery服务中添加卷挂载同步代码:
celery: build: context: ./Docker/celery container_name: celery depends_on: - redis volumes: - ./app/:/app # 同步Flask项目代码到Celery容器 restart: unless-stopped stdin_open: true networks: - api
5. Flask-Mail初始化问题
send_async_email中直接实例化Mail()但未关联Flask应用上下文,会导致Mail无法读取配置。需要在任务中手动初始化并配置:
@celery.task def send_async_email(email_data): from flask import Flask from flask_mail import Message, Mail app = Flask(__name__) # 从环境变量加载邮件配置 app.config['MAIL_SERVER'] = getenv('MAIL_SERVER') app.config['MAIL_PORT'] = int(getenv('MAIL_PORT', 587)) app.config['MAIL_USE_TLS'] = True app.config['MAIL_USERNAME'] = getenv('MAIL_USERNAME') app.config['MAIL_PASSWORD'] = getenv('MAIL_PASSWORD') mail = Mail(app) # 构造标准Message对象(flask_mail.send只接受Message实例) msg = Message( subject="Test Email", sender=email_data['sender'], recipients=email_data['recipients'], body=email_data['message'] ) mail.send(msg)
内容的提问来源于stack exchange,提问作者user2236794

