django使用celery

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功能,如若后续有用到新的功能,会及时更新。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值