文章目录
celery
快速入门
https://www.cnblogs.com/alex3714/p/6351797.html
问题纪录
KeyValueError:路径问题
https://blog.csdn.net/u014108439/article/details/83782531
ValueError安装包问题
https://blog.csdn.net/qq_30242609/article/details/79047660
AttributeError命令行路径问题
https://blog.csdn.net/weixin_43162402/article/details/83314692
简单配置
demo01.py
from celery import Celery
app = Celery('tasks',
broker='redis://127.0.0.1:6379/2', # 配置broker 发送到哪
backend='redis://127.0.0.1:6379/3') # 配置backend 结果返回到哪
@app.task(name='celerylearn.celeryServer.demo01.add')
def add(x, y):
print("running...", x, y)
return x + y
@app.task
def cmd(cmd_str):
print('running cmd',cmd_str)
# 在终端启动celery -A tasks worker --loglevel=info
# celery -A demo01 worker --loglevel=info
# celery -A demo01 worker -l info -P eventlet
终端运行
celery -A demo01 worker -l info -P eventlet
将任务推送给worker执行
from celerylearn.celeryServer.demo01 import add
# print(add(4,5))
add.delay(45,2)
Django + celery + Rabbitmq/redis
https://zhuanlan.zhihu.com/p/43060507
https://cloud.tencent.com/developer/article/1354471
官方文档 https://docs.celeryproject.org/en/latest/django/first-steps-with-django.html
Django celery中的配置(任选一种方式即可)
settings中的配置—当启动匿名默认的celery时读取该配置
import djcelery
# ==========
# = Celery =
# ==========
djcelery.setup_loader()
# BROKER_URL = 'amqp://guest:guest@localhost:5672//'
CELERY_ACCEPT_CONTENT = ['json']
CELERY_TASK_SERIALIZER = 'json'
CELERY_TIMEZONE = 'Asia/Shanghai' # 时区
CELERYBEAT_SCHEDULER = 'djcelery.schedulers.DatabaseScheduler' # 监测数据库信息
CELERY_ENABLE_UTC = False # 禁用UTC时间
BROKER_URL = 'amqp://root:root@192.168.5.224:5672' # 中间人地址
CELERY_IMPORTS = ("user.tasks",) # 引入任务包,默认会自动读取,找不到时可以手动引入
celery.py中的配置—当指定APP名称时读取该配置
from __future__ import absolute_import, unicode_literals
import os
from celery import Celery
from django.conf import settings
# set the default Django settings module for the 'celery' program.
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'django_server.settings')
app = Celery('project')
# Using a string here means the worker don't have to serialize
# the configuration object to child processes.
# - namespace='CELERY' means all celery-related configuration keys
# should have a `CELERY_` prefix.
app.config_from_object('django.conf:settings')
# app.config_from_object('django.conf:settings', namespace='CELERY')
# Load task modules from all registered Django app configs.
app.autodiscover_tasks(lambda: settings.INSTALLED_APPS)
@app.task(bind=True)
def debug_task(self):
print('Request: {0!r}'.format(self.request))
djcelery 数据库和代码
再导入djcelery并使用migrate之后,会产生4张表
django celery启动方式
- 启动的方式有2种,一种是指定APP名称,一直是使用默认匿名
- 指定APP名称,需要在主工程目录下加入celery.py文件,会读取这个APP的配置,作为celery的主动配置
- 不指定APP名称,会创建默认匿名的Celery APP,它读取的是settings.py中配置。
# django_server是包含celery.py的文件夹
# -A 是app名称
python manage.py celery -A django_server worker -l info
# 启动心跳服务
python manage.py celery beat
# 启动flower
python manage.py celery -A django_server flower -l info
# 启动完成后的任务名
django_server.apps.news.tasks.mul
# 不指定APP,使用默认的app启动,读取的是django settings中的配置信息
python manage.py celery worker
python manage.py celery flower -l info
# rabbitmq指定队列启动,只读取传入到该队列的信息
python manage.py celery worker -Q build-worker -l INFO
# rabbitmq启动flower
python manage.py celery flower --broker=amqp://root:root@192.168.5.223:5672//
# 指定队列和名称
python manage.py celery worker -A central_celery_service -Q build-worker -l INFO
python manage.py celery beat -A central_celery_service
python manage.py celery worker -Q tjgoverment -l INFO
启动时配置的参数
- base 参数
http://docs.jinkan.org/docs/celery/reference/celery.bin.base.html?highlight=a#cmdoption-A - worker 参数
http://docs.jinkan.org/docs/celery/reference/celery.bin.worker.html?highlight=#cmdoption-celery-worker–autoscale
--purge 使用这个参数会将清空消息对列中还未执行的任务,但是一般不会推荐这么做
celery的任务配置
不管是哪种启动方式,都会读取到的任务列表都说相同的。而在配置任务的时候,也会有2种方式,但本质都是相同的。
在app中加入tasks.py
文件,不用配置,celery会自动读取该文件。
任选一种即可
1.使用@shared_task配置函数任务
name表示该任务的名称
from __future__ import absolute_import, unicode_literals
import random
import time
from billiard import SoftTimeLimitExceeded
from celery import shared_task, Task
@shared_task(name='news.tasks.add')
def add(x, y):
time.sleep(10)
return x + y
@shared_task(name='news.tasks.mul')
def mul(x, y):
return x * y
2.使用Task,继承Task类
class KimiDivision(Task):
name = 'kimiDivision'
def run(self, *args, **kwargs):
print('10分钟一次执行了kiMiDivision............')
try:
choice = random.randint(0, 9)
if choice < 7:
print('开始sleep......')
time.sleep(10)
except Exception as exc:
print('-------开始retry')
raise self.retry(countdown=3, exc=exc)
return 666 // 111
更多share_task和Task中的配置参数,参考官网
http://docs.jinkan.org/docs/celery/reference/celery.app.task.html?highlight=name#id1
调用Celery任务
异步调用
参考 https://www.cnblogs.com/cwp-bg/p/10575688.html
add.apply_async((worker.id,), queue='build-worker')
- delay
- send_task
直接调用
参考 http://docs.jinkan.org/docs/celery/reference/celery.app.task.html?highlight=apply_async#celery.app.task.Task.apply_async
- apply
调用定时任务
任选一种
1.在settings
中进行配置
CELERYBEAT_SCHEDULE = { #定时器策略
#定时任务一: 每隔15s运行一次
# u'定时器bulletin': {
# "task": "user.tasks.bulletin",
# "schedule": crontab(hour=9, minute=00, ), # or 'schedule': timedelta(seconds=3),
# # "schedule":timedelta(seconds=5),
# "args": (),
# },
# # 定时任务二: 每隔12s运行一次
# u'定时器org': {
# "task": "user.tasks.org",
# "schedule": crontab(hour=9, minute=00, ), # or 'schedule': timedelta(seconds=3),
# # "schedule": timedelta(seconds=10),
# "args": (),
# },
u'定时器xsum': {
"task": "news.tasks.xsum",
"schedule": timedelta(seconds=6), # or 'schedule': ,
# "schedule":timedelta(seconds=5),
"args": (),
},
}
本质就是将Setting中的任务存贮到数据库中然后继续读取。
2.使用django admin在数据表中进行配置
补充:
在djcelery源码models.py中,
PeriodicTask表用于存放定时任务,当这个表发生更新或删除操作时,会发送信号:
signals.pre_delete.connect(PeriodicTasks.changed, sender=PeriodicTask)
signals.pre_save.connect(PeriodicTasks.changed, sender=PeriodicTask)
更新PeriodicTasks表
class PeriodicTasks(models.Model):
ident = models.SmallIntegerField(default=1, primary_key=True, unique=True)
last_update = models.DateTimeField(null=False)
objects = managers.ExtendedManager()
@classmethod
def changed(cls, instance, **kwargs):
if not instance.no_changes:
cls.objects.update_or_create(ident=1,
defaults={'last_update': now()})
@classmethod
def last_change(cls):
try:
return cls.objects.get(ident=1).last_update
except cls.DoesNotExist:
pass
而这个表,是影响celery beat是否监测到任务变化的关键所在。因此,当我们在代码中执行
PeriodicTask.objects.filter(queue=worker.queue).update(enabled=False)
时,它的意义就是让告诉celery beat心跳服务,任务已经发生改变,需要重新读取定时任务。
celery定时任务的时间限制
在setting.py中配置
- CELERYD_TASK_TIME_LIMIT
CELERYD_TASK_TIME_LIMIT # 时间限制,会杀死执行该任务的worker的进程然后重新开启worker。
简而言之,超时的任务会被杀死
Task hard time limit in seconds. The worker processing the task will be killed and replaced with a new one when this is exceeded.
- CELERYD_TASK_SOFT_TIME_LIMIT
CELERYD_TASK_SOFT_TIME_LIMIT
# 相对温柔一点的处理方式,超时的任务不会被立马杀死,
而是会抛出SoftTimeLimitExceeded异常,我们可以手动捕获该异常,然后自己进行超时的处理。
官网给出的解释如下:
The SoftTimeLimitExceeded exception will be raised when this is exceeded. The task can catch this to e.g. clean up before the hard time limit comes.
from celery.exceptions import SoftTimeLimitExceeded
@app.task
def mytask():
try:
return do_work()
except SoftTimeLimitExceeded:
cleanup_in_a_hurry()
以上,是我暂时用到的celery功能,如若后续有用到新的功能,会及时更新。