【RealEarthStudio】使用Celery+Redis执行异步任务
2026/5/2 10:59:14 网站建设 项目流程

使用 Celery + Redis 执行异步任务

  • 引言
  • 一、主要功能
  • 二、实现步骤
    • 2.1 安装依赖
    • 2.2 配置 Celery
    • 2.3 在 settings.py 中配置 Celery
    • 2.4 定义异步任务(tasks.py)
    • 2.5 在 View 中触发任务
    • 2.6 错误修正

引言

计算机专业硕士在读,主要研究方向是特定目标大斜视角目标检测与定位。因为要做的是特定目标,公开数据集较少,经过多方考虑还是决定要自建数据集。最终考虑的解决方案还是Blender+Python API的方式,项目起名叫RealEarthStudio
这系列文章主要对开发过程进行记录,方便我个人后续查看,也给相类似方向的同学提供一个思路。

【项目目录】:项目目录链接


一、主要功能

  • 功能:使用 Celery + Redis 执行异步任务。
  • 背景:上一篇文章已经安装了Redis数据库,现在配置Celery将耗时任务交给后台工作进程处理。
  • 效果
  • 码云项目链接:https://gitee.com/charlsewyq/RealEarthStudio

二、实现步骤

2.1 安装依赖

在命令行中运行:

pipinstallcelery redis django-celery-results

2.2 配置 Celery

  • 在项目根目录(与settings.py同级)创建celery.py
importosfromceleryimportCelery# 设置 Django 的默认设置模块os.environ.setdefault('DJANGO_SETTINGS_MODULE','RealEarthStudio.settings')app=Celery('RealEarthStudio')# 从 Django settings 中加载配置,以 CELERY_ 开头的项app.config_from_object('django.conf:settings',namespace='CELERY')# 自动发现各 app 下的 tasks.pyapp.autodiscover_tasks()
  • 修改__init__.py(确保 Celery 应用被加载):
from.celeryimportappascelery_app __all__=('celery_app',)

2.3 在 settings.py 中配置 Celery

# Celery 配置CELERY_BROKER_URL='redis://127.0.0.1:6379/0'# 使用 Redis 作为消息代理CELERY_RESULT_BACKEND='django-db'# 将任务结果存入数据库(需安装 django-celery-results)CELERY_ACCEPT_CONTENT=['json']CELERY_TASK_SERIALIZER='json'CELERY_RESULT_SERIALIZER='json'CELERY_TIMEZONE='Asia/Shanghai'# 如果使用 django-celery-results,添加到 INSTALLED_APPSINSTALLED_APPS+=['django_celery_results',]

2.4 定义异步任务(tasks.py)

app中新建tasks.py文件:

fromceleryimportshared_taskimportosfromdjango.utilsimporttimezonefrom.modelsimportRenderingTask@shared_taskdefexecute_render_task(render_id):""" 异步执行渲染任务 """try:render_task=RenderingTask.objects.get(render_id=render_id)# 写入信息文件full_filepath=os.path.join(render_task.rendered_result_dir.path,"info.txt")withopen(full_filepath,'w',encoding='utf-8')asf:f.write(f"=== 渲染任务信息 ===\n")f.write(f"渲染任务ID:{render_task.render_id}\n")f.write(f"渲染时间:{render_task.render_time.astimezone(timezone.get_default_timezone())}\n")f.write(f"渲染器类型:{render_task.renderer_type}\n")f.write(f"图像分辨率:{render_task.image_width}×{render_task.image_height}\n")f.write(f"总像素数:{render_task.image_pixels}\n\n")f.write(f"=== 模型信息 ===\n")f.write(f"目标模型数量:{render_task.target_models.count()}\n")ifrender_task.target_models.exists():fori,target_modelinenumerate(render_task.target_models.all(),1):all_categories=set(target_model.category.all())forcatinlist(all_categories):parent=cat.parentwhileparent:all_categories.add(parent)parent=parent.parent category_names=", ".join([str(cat.name)forcatinall_categories])f.write(f" 目标模型{i}:{target_model.model_id}({category_names})\n")f.write(f"场景模型数量:{render_task.scene_models.count()}\n")ifrender_task.scene_models.exists():fori,scene_modelinenumerate(render_task.scene_models.all(),1):category_names=", ".join([str(cat.name)forcatinscene_model.category.all()])f.write(f" 场景模型{i}:{scene_model.model_id}({category_names})\n")f.write(f"\n=== 光照参数 ===\n")f.write(f"日光方位角:{render_task.sun_azimuth}°\n")f.write(f"日光高低角:{render_task.sun_elevation}°\n\n")f.write(f"=== 相机参数 ===\n")f.write(f"相机距离列表:{render_task.camera_distances}\n")f.write(f"相机高低角列表:{render_task.camera_elevations}\n")f.write(f"相机方位角间隔:{render_task.camera_rotation_step}°\n\n")returnf"渲染任务{render_id}完成"exceptExceptionase:# 处理错误情况returnf"渲染任务{render_id}失败:{str(e)}"

2.5 在 View 中触发任务

app中修改views.py文件:

fromrest_framework.viewsimportAPIViewfrom.tasksimportexecute_render_taskfromutils.statusimportresponseasmy_responseclassStartRender(APIView):@staticmethoddefget(request,render_id):# 开始渲染print('准备启动渲染任务')execute_render_task.delay(render_id)print('渲染任务启动完成')# 返回信息data={"render_id":render_id,# "render_time": render_task.render_time,}returnmy_response.success(data=data,message="开始渲染")

2.6 错误修正

我在做的时候出现一个问题。查看Celery日志:

celery -A RealEarthStudio worker --loglevel=info

报错:

[2025-12-17 11:55:37,580: ERROR/MainProcess] Task handler raised error: ValueError(‘not enough values to unpack (expected 3, got 0)’)
billiard.einfo.RemoteTraceback:
Traceback (most recent call last):
File “D:\ProgramData\anaconda3\envs\realearthstudio_env\Lib\site-packages\billiard\pool.py”, line 362, in workloop
result = (True, prepare_result(fun(*args, **kwargs)))
^^^^^^^^^^^^^^^^^^^^
File “D:\ProgramData\anaconda3\envs\realearthstudio_env\Lib\site-packages\celery\app\trace.py”, line 683, in fast_trace_task
tasks, accept, hostname = _loc
^^^^^^^^^^^^^^^^^^^^^^^
ValueError: not enough values to unpack (expected 3, got 0)
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File “D:\ProgramData\anaconda3\envs\realearthstudio_env\Lib\site-packages\billiard\pool.py”, line 362, in workloop
result = (True, prepare_result(fun(*args, **kwargs)))
^^^^^^^^^^^^^^^^^^^^
File “D:\ProgramData\anaconda3\envs\realearthstudio_env\Lib\site-packages\celery\app\trace.py”, line 683, in fast_trace_task
tasks, accept, hostname = _loc
^^^^^^^^^^^^^^^^^^^^^^^
ValueError: not enough values to unpack (expected 3, got 0)

这个报错点在celery/app/trace.pyfast_trace_task,在 Windows 上非常常见,本质通常不是你任务函数写错了,而是 Celery 在 Windows 的多进程(billiard/prefork)兼容性问题导致 worker 内部的局部变量_loc没有被正确初始化,于是解包失败。Celery 官方文档也明确提到不支持 Microsoft Windows。

  • 解决方案:Windows 下用solo池跑 worker,在settings.py中添加
CELERY_WORKER_POOL="solo"

即可正常运行。


需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询