Celery-介绍
文档:http://docs.jinkan.org/docs/celery/getting-started/introduction.html 3.1.7
文档:https://www.celerycn.io/ru-men/celery-jian-jie 4.3.0
文档:https://docs.celeryq.dev/en/stable/userguide/application.html 最新
| 是一个简单,灵活可靠,处理大量消息的分布式系统,专注于实时处理异步任务,同时支持任务调度 |
| |
| 分布式系统是什么: |
| 比如:数据库,消息中间件,web后端,web前端,中间件系统,架构在不同的服务器上,也就是多台服务器组成的一个整体的系统应用。 |
| |
| Celery架构分为: |
| 1.消息中间件(message broker) |
| 2.任务执行单元(worker) |
| 3.任务结果储存(task resuit store) |
| |
| 1.消息中间件:(借助第三方) |
| celery不提供消息信息服务,但是可以与第三方进行继承中间件,Rabbitmq Redis MongoDB SQLAlchemy等 |
| |
| 2.任务执行单元:(执行任务) |
| worker是crlery提供的任务执行单元,worker并发的运行在分布式的节点中 |
| |
| 3.任务储存单元: (存储执行的任务结果) |
| task resuit store 用来存储worker的执行结果,crlery同时支持不同方式存储任务结果,包括AMQP,redis等 |
| |
| |
| 支持不同的并发手段和序列化手段 |
| 并发:Prefork, Eventlet, gevent, threads/single threaded |
| 序列化:pickle, json, yaml, msgpack. zlib, bzip2 compression, Cryptographic message signing 等等 |
| 1. user 用户 通过自己 商场web 购买产品 (在自商场web中发起请求对该商品进行下单操作) |
| |
| 下单后需要通过 (短信或者邮件的方式通知,用于下单成功,商品当前状态) |
| |
| 2. 因为对邮件提醒与短信提醒操作耗时较长,所以需要将这部分的从操作存放在'消息队列(AMQP broker)中间件代理'中 |
| |
| 3. celery worker,就会监听当前中间件队列中得任务,从中拿到任务,进行执行 |
| # 注意: 因为如果是同不得方式进行获取中间件队列的任务(任务又是 io事件 较长的操作) 那么就造成执行过长(尤其是遇到大量的用访问时),解决方式: 多线程 多进程 协程 等方式并发解决(能处理,但是比较复杂)。 而celery可以简单进行处理大量的io操作(是在并发的基础上进行的封装) |
| 上面的图片中1 ... n 的操作就是celery从队列中进行任务的异步并发执行,1...n那么就是n个任务(n个线程)n个worker,(进程 + 协程的方式进行执行 更为高效) |
| |
| 4. 执行完毕任务,会将任务结果存储到 task resuit store (任务存储器中),如果需要结果,就可以从任务存储器中获取当前的结果 |
使用场景
| celery是一个强大的 分布式任务队列的异步处理框架,它可以让任务的执行完全脱离主程序,甚至可以被分配到其他主机上运行。我们通常使用它来实现异步任务(async task)和定时任务(crontab)。 |
| |
| 1.异步任务:将耗时操作任务提交给Celery去异步执行,比如发送短信/邮件、消息推送、音视频处理等等 |
| |
| 2.定时任务:定时执行某件事情,比如每天数据统计 |
优点
| 1.Celery 使用和维护都非常简单,并且不需要配置文件。 |
| |
| 2.woker和client会在网络连接丢失或者失败时,自动进行重试。并且有的brokers 也支持“双主”或者“主/从”的方式实现高可用。 |
| |
| 3.单个的Celery进程每分钟可以处理百万级的任务,并且只需要毫秒级的往返延迟(使用 RabbitMQ, librabbitmq, 和优化设置时) |
| |
| 4.Celery几乎每个部分都可以扩展使用,自定义池实现、序列化、压缩方案、日志记录、调度器、消费者、生产者、broker传输等等 |
| |
| Celery使用的是基于消费者与生产者模型 |
| |
| 是异步执行的,同时执行多个任务 |
Celery-基本功能
| 1.安装 |
| pip install -U Celery 最新版 |
| |
| win系统不支持 celery |
| pip install eventlet |
| |
| |
| 2.常用配置项(大写变量在新版本已经弃用,请使用对应小写) |
| |
| 关于配置的官方文档: |
| https://docs.celeryq.dev/en/stable/userguide/configuration.html |
| |
| 1.中间件链接 |
| BROKER_URL = 'redis://127.0.0.1:6379/0' |
| |
| 2.指定结果的接收地址 |
| CELERY_RESULT_BACKEND = 'redis://127.0.0.1:6379/1' |
| |
| 3.指定任务序列化方式 默认json |
| CELERY_TASK_SERIALIZER = 'msgpack' |
| |
| 4.指定结果序列化方式 默认 json |
| CELERY_RESULT_SERIALIZER = 'msgpack' |
| |
| 5.指定任务接受的序列化类型. 默认['json'] |
| CELERY_ACCEPT_CONTENT = ['msgpack'] |
| |
| 6.任务过期时间,celery任务执行结果的超时时间 默认为1天时间 |
| CELERY_TASK_RESULT_EXPIRES = 24 * 60 * 60 |
| |
| 7.任务发送完成是否需要确认,对性能会稍有影响 默认为False |
| CELERY_ACKS_LATE = True |
| |
| 8.压缩方案选择,可以是zlib, bzip2,默认是发送没有压缩的数据 默认为None未设置 |
| CELERY_MESSAGE_COMPRESSION = 'zlib' |
| |
| 9.规定完成任务的时间 单个任务的运行时间限制,否则会被杀死 |
| 在5s内完成任务,否则执行该任务的worker将被杀死,任务移交给父进程 默认为None未设置 |
| CELERYD_TASK_TIME_LIMIT = 5 |
| |
| 10.c1elery worker的并发数,默认是服务器的内核数目,也是命令行-c参数指定的数目 默认为None未设置全部 |
| CELERYD_CONCURRENCY = 4 |
| |
| 11.celery worker 每次去BROKER中预取任务的数量 默认为4 |
| CELERYD_PREFETCH_MULTIPLIER = 4 |
| |
| 12.每个worker执行了多少任务就会死掉,默认是无限的 |
| CELERYD_MAX_TASKS_PER_CHILD = 40 |
| |
| 13.设置当前celery时区 |
| CELERY_TIMEZONE = "Asia/Shanghai" |
| |
| 14.如果启用,消息中的日期和时间将转换为使用 UTC 时区。 |
| CELERY_ENABLE_UTC = True |
| |
| 15.用于向后兼容性 当设置时 如果为 false,则改用系统本地时区。 |
| CELERY_ENABLE_UTC = False |
| |
| |
| 3.设置配置的方式: |
| app = Celery('task') |
| |
| app.conf.task_serializer = 'json' |
| |
| app.conf.update( |
| task_serializer='json', |
| accept_content=['json'], |
| result_serializer='json', |
| timezone='Europe/Oslo', |
| enable_utc=True, |
| ) |
| |
| |
| celeryconfig.py |
| task_serializer='json' |
| app.config_from_object('celeryconfig') |
| |
| 4.创建celery任务 |
| app = Celery('task') |
| @app.task |
| def func('接受的参数'): |
| pass |
| |
| 5.执行执行celery脚本 |
| |
| |
| 执行命令 |
| -l info 打印日志登记 |
| --app app(脚本) == -A app(脚本) 启动脚本 |
| -P gevent |
| |
| |
| celery --app app(脚本名称) worker -l info -P gevent |
| celery -A app(脚本名称) worker -l info -P gevent |
| |
| |
| |
| 6.执行任务 |
| a = func.delay('传入的参数') |
| a.id |
Celery-单级目录(简单)
目录结构
| celery_app |
| -app.py |
| -config.py |
| -task.py |
| -main.py |
| -result.py |
| |
| |
| |
| 1.需要将celery_app路径 加入到python找包的列表(sys.path)中 |
| celery -A app worker -l info -P gevent |
| |
| 2.如果没有将celery_app路径 加入到python找包的列表(sys.path)中 |
| celery -A task worker -l info -P gevent |
| |
| |
| 如果没有加入python找包的列表中(sys.path),执行:[celery -A app worker -l info -P gevent ] 就会出现 ModuleNotFoundError错误,本人通过pycharm创建项目,pycharm会自动的将最外层包添加到(sys.path)python找包的列表中,通过cmd执行,脱离了pycharm无法找到 |
app.py
| import os |
| import sys |
| from celery import Celery |
| |
| sys.path.append(os.path.abspath(os.path.dirname(__name__))) |
| |
| app = Celery('task') |
| |
| |
| app.config_from_object('config') |
| |
| |
| app.autodiscover_tasks(['task']) |
| |
config.py
| |
| BROKER_URL = 'redis://127.0.0.1:6379/2' |
| |
| |
| CELERY_RESULT_BACKEND = 'redis://127.0.0.1:6379/1' |
| |
| |
| CELERY_TIMEZONE = "Asia/Shanghai" |
| |
task.py
| from app import app as celery_app |
| import time |
| |
| |
| |
| |
| @celery_app.task |
| def send_weixin(name): |
| print(f'开始发送微信---发送者{name}') |
| time.sleep(3) |
| print('微信发送成功') |
| return 'ok' |
| |
| |
| |
| @celery_app.task |
| def send_email(name): |
| print(f'开始发送邮件---发送者{name}') |
| time.sleep(3) |
| print('邮件发送完成') |
| return 'ok' |
main.py
| from task import * |
| |
| |
| |
| |
| |
| result_email = send_email.delay('haha') |
| print(result_email.id) |
| |
| result_weixin = send_weixin.delay('hah') |
| print(result_weixin.id) |
| |
| |
result.py
| from app import app as celery_app |
| |
| from celery.result import AsyncResult |
| |
| |
| |
| async_result = AsyncResult(id='de187cb2-51b5-4a66-92ca-8ca96b60d1f0', app=celery_app) |
| |
| |
| if async_result.successful(): |
| result = async_result.get() |
| print(result) |
| elif async_result.failed(): |
| print('执行失败') |
| elif async_result.status == 'PENDING': |
| print('任务正在等待执行') |
| elif async_result.status == 'RETRY': |
| print('该任务将重试,可能是因为失败。') |
| elif async_result.status == 'STARTED': |
| print('任务已启动') |
| elif async_result.status == 'FAILURE': |
| print('任务引发异常,或已超出重试限制') |
| elif async_result.status == 'SUCCESS': |
| print('任务已成功执行') |
Celery-多级目录

| task_cel |
| -celery_tasks # 消费者 |
| - __init__.py # win需要将实例对象放在__init__中 |
| - celery.py # celery实例化对象 win不可用(会出现找不模块的现象) |
| - task01.py # 不同的任务类型存放在不同的任务目录下 |
| - task02.py # |
| check_result.py # 获取执行任务执行的结果 |
| produce_task.py # 执行celery任务 |
| |
| |
| 如果使用celery.py文件创建celery实例对象的情况下: |
| # 在win 环境下无法启动 出现了ModuleNotFoundError(报错找不模块或者找到路径或者没法办发现任务) |
| celery -A celery_tasks.celery worker -l info -P gevent |
| |
| 在win环境下使用多级目录建议使用__init__.py |
| 当使用celery_tasks就会自动触发init.py文件的代码 |
| celery -A celery_tasks worker -l info -P gevent |
| |
| # 执行命令时需要在task_cel文件夹下执行 |
| celery -A celery_tasks worker -l info -P gevent |
--init--.py
| import os |
| import sys |
| from celery import Celery |
| |
| sys.path.append(os.path.abspath(os.path.dirname(__name__))) |
| |
| # 1.可以添加 Celery(include=['celery_tasks.task01','celery_tasks.task02']) |
| # 2.也可以使用autodiscover_tasks(['celery_tasks.task01','celery_tasks.task02']) 自动扫描 |
| # 3.也可以使用在配置文件中 imports = ( |
| 'celery_app.task1', |
| 'celery_app.task2', |
| ) |
| app = Celery('celery_dom', ) |
| |
| # 设置配置 |
| app.conf.update( |
| broker_url='redis://127.0.0.1:6379/1', |
| result_backend='redis://127.0.0.1:6379/2', |
| timezone='Asia/Shanghai' |
| ) |
| |
| # 自动发现任务,扫描任务, |
| app.autodiscover_tasks(['celery_tasks.task01', 'celery_tasks.task02']) |
task01.py与task02.py
| from celery_tasks import app |
| import time |
| |
| |
| |
| @app.task |
| def send_email(name): |
| print(f'开始发送邮件----发送人{name}') |
| time.sleep(3) |
| print('发送完成') |
| |
| return '666' |
| |
| |
| @app.task |
| def send_wixin(name): |
| print(f'开始发送微信----发送人{name}') |
| time.sleep(4) |
| print('发送完成') |
| |
| return '666' |
check_result.py
| from celery_tasks.task01 import send_email |
| from celery_tasks.task02 import send_wixin |
| |
| |
| result1 = send_email.delay('啊哈') |
| print(result1.id) |
| result2 = send_wixin.delay('啊哈') |
| print(result1.id) |
check_result.py
| from celery_tasks import app as celery_app |
| |
| from celery.result import AsyncResult |
| |
| |
| |
| async_result = AsyncResult(id='c296cb30-0a1e-4b62-be00-c2529c5ab434', app=celery_app) |
| |
| |
| if async_result.successful(): |
| result = async_result.get() |
| print(result) |
| elif async_result.failed(): |
| print('执行失败') |
| elif async_result.status == 'PENDING': |
| print('任务正在等待执行') |
| elif async_result.status == 'RETRY': |
| print('该任务将重试,可能是因为失败。') |
| elif async_result.status == 'STARTED': |
| print('任务已启动') |
| elif async_result.status == 'FAILURE': |
| print('任务引发异常,或已超出重试限制') |
| elif async_result.status == 'SUCCESS': |
| print('任务已成功执行') |
Celery-定时功能
Celery-单级目录(简单)
目录结构
| celery_dom |
| -main.py |
| -task.py |
| -produce.py |
main.py
| from celery import Celery |
| import os |
| import sys |
| # 将main的父级文件添加到sys.path中(一定要加 否者就会报找不文件包的错误) |
| path = os.path.abspath(os.path.dirname(__name__)) |
| sys.path.append(path) |
| |
| print(path) |
| |
| app = Celery('task') |
| |
| # 设置配置 |
| app.conf.update( |
| broker_url='redis://127.0.0.1:6379/1', |
| result_backend='redis://127.0.0.1:6379/2', |
| timezone='Asia/Shanghai') |
| # 自动搜索与发现任务 |
| app.autodiscover_tasks(['task']) |
task.py
| from main import app |
| |
| import time |
| |
| @app.task |
| def send_email(name): |
| print(f'当前人{name}发送邮件中') |
| time.sleep(3) |
| |
| print('发送完成') |
| |
| return 'ok' |
| |
| 任务 |
produce.py
| import datetime |
| from task import send_email |
| |
| |
| |
| v1 = datetime.datetime(2023, 2, 16, 22, 19) |
| print(v1) |
| |
| v2 = datetime.datetime.utcfromtimestamp(v1.timestamp()) |
| print(v2) |
| |
| |
| |
| result = send_email.apply_async(args=['哈哈',], eta=v2) |
| print(result.id) |
| |
| ''' |
| 当进行启动时, |
| 1.先回获取到当前任务id值 |
| 2.启动的celery就会处于挂起状态 相当于等待执行 |
| 3.当规定的定时时间到了,就会进行执行 |
| |
| |
| ''' |
| |
| |
| ctime = datetime.datetime.now() |
| |
| utc_ctime = datetime.datetime.utcfromtimestamp(ctime.timestamp()) |
| |
| from datetime import timedelta |
| time_delay = timedelta(seconds=10) |
| task_time = utc_ctime + time_delay |
| |
| |
| result = send_email.apply_async(args=['哈哈',], eta=task_time) |
| print(result.id) |
启动
| celery -A main worker -l info -P gevent |
Celery-多级目录执行
目录结构
| celery_app |
| - __init__.py |
| - task1.py |
| - task2.py |
命令说明:
| |
| |
| |
| 指令:celery -A <文件.py> worker -l info -P gevent -c 5 |
| |
| 1.1.这个命令启动消费者(celery服务),用来监听broker_url中间件队列中得任务。 |
| |
| 1.2.如果对了中没有任务这个生产者就会阻塞。 |
| |
| 1.3.当生产者(调用了celery的任务)触发任务,那么就会将任务存放到中间件队列中,那么消费者才能进行执行任务。 |
| |
| |
| |
| 指令celery -A <文件.py> beat -l info |
| |
| 2.1.当启动这条命令时,就会读取启动文件中得配置信息(beat_schedule配置信息),根据配置信息的时间进行调度任务 |
| |
| 2.2.这条命令就是将beat_schedule配置的任务调度器,根据时间设置,将任务添加到中间件队列中,让消费者进行执行 |
| |
| 2.3和第一条命令不同,属于根据任务调度器向队列中插入任务 |
| |
| 2.4如果当前命令启动,那么就会按照任务调度器时间周期向中间件队列中插入任务(如果不启动worker命令的话,那么队列中的任务就会一直存在,直到启动woker命令才会执行,除了清空队列) |
| |
| |
| |
init.py
| from datetime import timedelta |
| from celery import Celery |
| import os |
| import sys |
| path = os.path.abspath(os.path.dirname(__name__)) |
| sys.path.append(path) |
| |
| print(path) |
| |
| app = Celery('task') |
| |
| |
| app.conf.update( |
| broker_url='redis://127.0.0.1:6379/1', |
| result_backend='redis://127.0.0.1:6379/2', |
| timezone='Asia/Shanghai') |
| |
| app.autodiscover_tasks(['celery_app.task01', 'celery_app.task02']) |
| |
| |
| |
| |
| app.conf.beat_schedule = { |
| ''' |
| 配置参数说明 |
| "调度器任务的名称(随便起)见名知意":{ |
| 路径需要能让celery扫描或发现的任务 |
| 'task':"执行任务的函数路径", 例如:celery_app.task01.send_email |
| 'schedule':'执行的时间设置', |
| 'args':('传递的参数',) |
| } |
| ''' |
| "add_send_email_10": { |
| |
| 'task': 'celery_app.task01.send_email', |
| |
| |
| |
| 'schedule': timedelta(seconds=6), |
| |
| 'args': ('我是传递的参数',) |
| }, |
| |
| } |
task1.py 与 task2.py
| from celery_app import app |
| |
| import time |
| |
| |
| |
| @app.task |
| def send_email(name): |
| print(f'当前人{name}发送邮件中') |
| time.sleep(3) |
| |
| print('发送完成') |
| |
| return 'ok' |
| |
| |
| |
| @app.task |
| def send_wixin(name): |
| print(f'当前人{name}发送邮件中') |
| time.sleep(3) |
| |
| print('发送完成') |
| |
| return 'ok' |
执行命令
| 1.需要先执行celery监听队列的命令 |
| celery -A celery_app worker -l info -P gevent -c 5 |
| |
| |
| 2.在进行执行beat命令将任务存入队列中 |
| celery -A celery_app beat -l info |
| |
| |
| 原因: |
| 如果先执行beat命令,那么就会根据配置信息中的调度器时间将任务存储到中间件队列中那么就会在启动worker命令时间间隔内(如果时间比较短,忘记启动worker)存储了多条任务到队列中,就会立马发现队列中得任务并执行。 |
| |
| |
Django框架使用Celery
| 如果使用的不是一台服务器,而是多台,那么 |
| 消费者(celery)的任务代码要在生产者中有一份(一模一样) |
| 消费者监听中间件 |
| 生产者将任务传入中间件,消费者去执行 |
目录结构
| django_celery_dom |
| - app01 |
| ... |
| views.py |
| - django_celery_dom |
| .... |
| urls.py |
| - mycelery |
| __init__.py |
| config.py |
| main.py |
| sms |
| __init__.py |
| tasts.py |
| |
| 启动命令需要在django_celery_dom文件夹下进行启动 |
| celery -A mycelery worker -l info -P gevent |
| |
| |
| 注意: |
| 1.路径问题确定好路径(尤其是sys.path python寻找的路径) |
| 2.环境问题,celery对win不支持 |
views.py
| from django.shortcuts import render |
| from django.http import JsonResponse |
| |
| from mycelery.sms.tasks import send_email |
| def home(request): |
| |
| |
| send_email.delay() |
| |
| |
| |
| from datetime import datetime,timedelta |
| ctime = datetime.now() |
| |
| utc_ctime = datetime.utcfromtimestamp(ctime.timestamp()) |
| time_delay = timedelta(seconds=10) |
| task_time = utc_ctime + time_delay |
| result = send_email.apply_async([], eta=task_time) |
| print(result.id) |
| |
| return JsonResponse({'code':200}) |
urls.py
| from django.contrib import admin |
| from django.urls import path |
| from app01 import views |
| urlpatterns = [ |
| path('admin/', admin.site.urls), |
| path('home/',views.home) |
| ] |
mycelery.init.py
| ''' |
| 当前这包就是一个关于celery的程序包 |
| 不属于django的一部分,而是属于单独的一段程序 |
| ''' |
| import os |
| import sys |
| from celery import Celery |
| |
| |
| f_file = os.path.dirname(os.path.dirname(__name__)) |
| sys.path.append(f_file) |
| |
| |
| |
| |
| app = Celery('sms') |
| |
| |
| os.environ.setdefault('DJANGO_SETTINGS_MODULE','celeryPros.settings.dev') |
| |
| |
| app.config_from_object('mycelery.config') |
| |
| |
| |
| app.autodiscover_tasks(['mycelery.sms.tasks']) |
mycelery.config.py
| '''celery配置信息''' |
| |
| broker_url='redis://127.0.0.1:6379/1' |
| result_backend='redis://127.0.0.1:6379/2' |
mycelery.sms.tesks.py
| '''这是celery任务''' |
| |
| from mycelery import app |
| import time |
| |
| @app.task |
| def send_email(): |
| print('ok') |
| time.sleep(3) |
| print('完成') |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异