Celery Beat是Celery框架的一个组件,用于调度和执行周期性任务,它允许在指定的时间间隔内自动触发任务的运行,而无需手动触发。
定义周期性任务
可以通过在Celery应用程序中定义周期性任务来使用Celery Beat,这些任务通常定义为带有@periodic_task装饰器的函数,指定任务的执行时间间隔。
from celery import Celery
from celery.schedules import crontab
app = Celery('myapp', broker='amqp://guest@localhost//')
@app.on_after_configure.connect
def setup_periodic_tasks(sender, **kwargs):
# 每分钟执行一次任务
sender.add_periodic_task(60.0, my_task.s(), name='my_task')
# 每天凌晨执行任务
sender.add_periodic_task(
crontab(hour=0, minute=0),
my_daily_task.s(),
name='my_daily_task',
)
@app.task
def my_task():
print("执行周期性任务")
@app.task
def my_daily_task():
print("执行每日任务")
在上述示例中,setup_periodic_tasks函数使用add_periodic_task方法定义了两个周期性任务。my_task任务将每分钟执行一次,而my_daily_task任务将在每天凌晨执行一次。
时间表选项
Celery Beat支持多种时间表选项,可用于定义任务的执行时间间隔。常见的选项包括:
crontab:基于类似Cron表达式的调度规则,例如每天特定时间执行、每周特定天执行等。
solar:基于日出、日落等太阳事件的调度规则。
schedule:基于固定时间间隔的调度规则,例如每隔一段时间执行一次。
from celery.schedules import crontab, solar, schedule
# 使用 crontab
sender.add_periodic_task(crontab(hour=1, minute=0), my_task.s())
# 使用 solar
sender.add_periodic_task(solar('sunrise', -30), my_task.s())
# 使用 schedule
sender.add_periodic_task(schedule(run_every=timedelta(minutes=5)), my_task.s())
配置Celery Beat
可以通过配置文件或命令行参数来配置Celery Beat的行为。其中一种常见的配置方法是使用celeryconfig.py文件,该文件包含了Celery Beat的配置选项。
# celeryconfig.py
# 使用 Redis 作为调度器
broker_url = 'redis://localhost:6379/0'
# 指定时区
timezone = 'Asia/Shanghai'
# 指定任务模块
beat_schedule = {
'my_task': {
'task': 'myapp.my_task',
'schedule': crontab(hour=1, minute=0),
'args': (),
},
}
在上述示例中,我们使用Redis作为调度器,并配置了my_task任务的调度规则。
启动Celery Beat:要启动Celery Beat,可以使用celery beat命令。
celery -A myapp beat
这将启动Celery Beat进程,并根据配置文件中的设置调度和执行周期性任务。
Celery Beat是一个强大的工具,可用于自动调度和执行周期性任务。通过合理配置和使用Celery Beat,您可以轻松管理和执行定期运行的任务。
在 Celery 的 Beat 调度器中,你可以使用 crontab 和 timedelta 两种方式来定义定时任务的调度。
crontab:crontab 是一个类似于 UNIX 中的 cron 语法的调度器。它允许你以更精细的方式定义任务的执行时间。你可以指定分钟、小时、日期、月份和星期几等字段的值来创建复杂的调度规则。例如,每天的特定时间运行任务,每周的特定日期和时间运行任务等。
from celery.schedules import crontab
app.conf.beat_schedule = {
'task1': {
'task': 'my_app.my_task',
'schedule': crontab(minute=0, hour='*/2'), # 每隔两小时运行一次任务
},
}
timedelta:timedelta 是一个相对时间间隔的调度器。你可以指定一个时间间隔,例如每隔多少秒、分钟、小时、天等运行任务。相对于 crontab,timedelta 更适合简单的定时任务场景。
from datetime import timedelta
app.conf.beat_schedule = {
'task2': {
'task': 'my_app.my_task',
'schedule': timedelta(seconds=30), # 每隔30秒运行一次任务
},
}
总的来说,crontab 提供了更灵活和复杂的任务调度规则,可以精确到分钟和日期级别。而 timedelta 则适合简单的间隔时间调度,以秒、分钟、小时等为单位。
你可以根据任务的需求和调度规则选择合适的调度器类型,以实现准确和可靠的定时任务功能。
定时任务示例
使用celery实现一个周期性任务,每隔10s打印一次"hello,world"
1.创建一个目录my_scheduler,在该目录下创建两个文件
test/my_scheduler/celery.py
from celery import Celery
app = Celery('my_scheduler', broker='redis://127.0.0.1:6379/0', backend='redis://127.0.0.1:6379/0')
app.autodiscover_tasks(['my_scheduler.celery_task'])
app.conf.beat_schedule = {
'hello-every-10-seconds': {
'task': 'my_scheduler.celery_task.hello',
'schedule': 10.0,
},
}
test/my_scheduler/celery_task.py
from celery import Celery
from celery.schedules import crontab
# app = Celery('my_scheduler', broker='redis://localhost:6379/0')
from celery import shared_task
@shared_task
def hello():
print('Hello, world!')
2.在test下执行
celery --broker=redis://127.0.0.1:6379/0 -A my_scheduler beat
D:\program\python\work_use\test>celery --broker=redis://127.0.0.1:6379/0 -A my_scheduler beat
celery beat v5.2.7 (dawn-chorus) is starting.
__ - ... __ - _
LocalTime -> 2024-04-17 15:24:03
Configuration ->
. broker -> redis://127.0.0.1:6379/0
. loader -> celery.loaders.app.AppLoader
. scheduler -> celery.beat.PersistentScheduler
. db -> celerybeat-schedule
. logfile -> [stderr]@%WARNING
. maxinterval -> 5.00 minutes (300s)
3.在test目录下执行
celery -A my_scheduler worker -l info -P eventlet
celery -A my_scheduler worker -l info -P eventlet
D:\APP_install\python\lib\site-packages\requests\__init__.py:102: RequestsDependencyWarning: urllib3 (1.26.8) or chardet (5.1.0)/charset_normalizer (2.0.12) doesn't match a supported version!
warnings.warn("urllib3 ({}) or chardet ({})/charset_normalizer ({}) doesn't match a supported "
-------------- celery@DESKTOP-DJPRLQD v5.2.7 (dawn-chorus)
--- ***** -----
-- ******* ---- Windows-10-10.0.19041-SP0 2024-04-17 15:37:39
- *** --- * ---
- ** ---------- [config]
- ** ---------- .> app: my_scheduler:0x20a1ff8da00
- ** ---------- .> transport: redis://127.0.0.1:6379/0
- ** ---------- .> results: redis://127.0.0.1:6379/0
- *** --- * --- .> concurrency: 12 (eventlet)
-- ******* ---- .> task events: OFF (enable -E to monitor tasks in this worker)
--- ***** -----
-------------- [queues]
.> celery exchange=celery(direct) key=celery
[tasks]
. my_scheduler.celery_task.hello
[2024-04-17 15:37:39,394: INFO/MainProcess] Connected to redis://127.0.0.1:6379/0
[2024-04-17 15:37:39,417: INFO/MainProcess] mingle: searching for neighbors
[2024-04-17 15:37:40,442: INFO/MainProcess] mingle: all alone
[2024-04-17 15:37:40,447: INFO/MainProcess] pidbox: Connected to redis://127.0.0.1:6379/0.
[2024-04-17 15:37:40,450: INFO/MainProcess] celery@DESKTOP-DJPRLQD ready.
[2024-04-17 15:37:40,591: INFO/MainProcess] Task my_scheduler.celery_task.hello[d225f94b-1c2f-4bd0-9bda-a080c996434b] received
[2024-04-17 15:37:40,591: WARNING/MainProcess] Hello, world!
[2024-04-17 15:37:40,638: INFO/MainProcess] Task my_scheduler.celery_task.hello[208783fe-e88e-4ef4-8e74-49082e7c8f25] received
[2024-04-17 15:37:40,639: WARNING/MainProcess] Hello, world!
[2024-04-17 15:37:40,689: INFO/MainProcess] Task my_scheduler.celery_task.hello[d225f94b-1c2f-4bd0-9bda-a080c996434b] succeeded in 0.09399999999732245s: None
[2024-04-17 15:37:40,690: INFO/MainProcess] Task my_scheduler.celery_task.hello[208783fe-e88e-4ef4-8e74-49082e7c8f25] succeeded in 0.046999999991385266s: None
celery@DESKTOP-DJPRLQD ready
.就代表celery已经准备好处理task了,Task my_scheduler.celery_task.hello[208783fe-e88e-4ef4-8e74-49082e7c8f25] received
代表开始消费task了,在redis里面查询就有打印了:
127.0.0.1:6379> get celery-task-meta-208783fe-e88e-4ef4-8e74-49082e7c8f25
"{\"status\": \"SUCCESS\", \"result\": null, \"traceback\": null, \"children\": [], \"date_done\": \"2024-04-17T07:37:40.687011\", \"task_id\": \"208783fe-e88e-4ef4-8e74-49082e7c8f25\"}"
如果将定时任务修改有返回值:
@app.task
def hello():
print('Hello, world!')
return "hello world"
则在redis里面的result字段将保存返回值:
127.0.0.1:6379> get celery-task-meta-5f37e97d-10a7-43d3-84b4-f00613bf602d
"{\"status\": \"SUCCESS\", \"result\": \"hello world\", \"traceback\": null, \"children\": [], \"date_done\": \"2024-04-17T07:42:27.053864\", \"task_id\": \"5f37e97d-10a7-43d3-84b4-f00613bf602d\"}"
注意:
app.autodiscover_task()执行报错
Received unregistered task of type 'my_scheduler.celery_task.hello'.
The message has been ignored and discarded.
Did you remember to import the module containing this task?
Or maybe you're using relative imports?
Please see
http://docs.celeryq.org/en/latest/internals/protocol.html
for more information.
可以看到是因为发送的任务未注册导致,需要指定注册的目录:app.autodiscover_tasks([‘my_scheduler.celery_task’])