celery的使用

1,322 阅读5分钟

celery

celery是一个简单的、可定制的、可靠的用来处理大量信息的分布式系统,支持异步任务、定时任务。celery是一个分布式队列的管理工具,可以用celery的接口快速管理一个分布式的任务队列。它本身不是任务,是管理工具,封装了任务管理的常见操作。

首先你需要安装celery,最新版本为4.3,如果使用redis作为broker,推荐使用4.1版本,因为4.3版本可能与django-redis-cache的版本有兼容问题,解决这个问题所需要的模块如下

pip install celery==4.1.0
pip install django-redis-cache==2.10.6
pip install django-celery-results==1.1.2
pip install redis==3.2.0

celery的组件

1. celery使用生产者消费者设计模式

  • Celery Beat:任务调度器。Beat进程会读取配置文件内容,周期性的将配置中到期需要执行的任务发送给任务队列
  • Celery Worker:执行任务的消费者,通常会在后台服务器运行多个消费者,提高运行效率
  • Broker:消息队列,队列本身,也成为消息中间件,接受任务生产者发送过来的任务消息,存进队列分发给任务消费方(通常是消息队列或者数据库)
  • Producer:任务生产者,调用Celery API,函数或者装饰器,产生任务交给任务队列的都是任务生产者
  • Result Backend:任务处理完成后保存状态信息和结果,以供查询

Celery框架图:

graph TD
A[任务发布者 Producer]-->B[任务调度 Celery Beat]
C[消息代理 Broker]-->B
B-->D[任务消费者 Worker]
B-->E[任务消费者 Worker]
B-->F[任务消费者 Worker]
D-->G[存储结果 数据库]
E-->G
F-->G

2. 产生任务的方式

  1. 发布者发布任务(WEB应用)
  2. 任务调度发布任务(定时任务)

3. celery依赖三个库

  • billiard:基于Python2.7的multisuprocessing改进的库,主要用来提高性能和稳定性
  • librabbitmp:c语言实现的Python客户端
  • kombu:Celery自带的用来收发消息的库,提供了符合Python语言习惯的,使用AMQP协议的高级接口

消息代理Broker

使用RabbitMQ和Redis

使用RabbitMQ来作为代理:

sudo apt-get install rabbitmq-server

或者在Docker里面运行:

docker run -d -p 5462:5462 rabbitmq

使用Redis作为代理:

# 在Docker里面运行
docker run -d -p 6379:6379 redis

使用redis作为代理的具体配置。首先需要一个模块celery[redis]

pip install -U "celery[redis]"

然后设置消息队列存储的地址:

app.conf.broker_url = 'redis://localhost:6379/0'
# 格式如下
# redis://:password@hostname:port/db_number

基本用法

我们可以直接创建一个tasks.py来编写消息队列

from celery import Celery

app = Celery('tasks', broker='pyamqp://guest@localhost//')

@app.task
def add(x, y):
    return x + y

这是一个基本的celery消息队列的写法,celery的基本使用流程如下:

  • 创建Celery的app
  • 指定消息代理Broker
  • 创建任务
  • 添加任务到任务队列
  • 配置执行策略

与Django

在Django中使用Celery,可以将配置集中写入到settings.py里面。

注意:celery4.0支持Django1.8以上的版本,如果是以下的版本请使用celery3.1。

app

首先是创建一个Celery的app,在Django项目目录下创建/proj/proj/celery.py的文件

from __future__ import absolute_import, unicode_literals
import os
from celery import Celery

# set the default Django settings module for the 'celery' program.
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'proj.settings')
# it`s not necessary to have many instances of the library, one is enough
app = Celery('proj')

# Using a string here means the worker doesn'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', namespace='CELERY')

# Load task modules from all registered Django app configs.
app.autodiscover_tasks()


@app.task(bind=True)
def debug_task(self):
    print('Request: {0!r}'.format(self.request))

注册

然后将这个app注册到/proj/proj/__init__.py文件中,可以确保在Django启动时,celery app会被加载:

from __future__ import absolute_import, unicode_literals

# This will make sure the app is always imported when
# Django starts so that shared_task will use this app.
from .celery import app as celery_app

__all__ = ('celery_app',)

创建

在每个应用的目录下,创建文件/app/tasks.py,然后使用@shared_task装饰器来创建任务

from celery import shared_task


@shared_task
def xsum(numbers):
    return sum(numbers)

消息队列

results

任务的运行信息会被存储到默认的数据库sqlite里面,将其改为默认的数据库,使用了一个模块django-celery-results

pip install django-celery-results安装之后在settings.py里面注册app

INSTALLED_APPS = (
	...,
    'django_celery_results'
)

迁移这个app,使其在生成数据库映射

python manage.py migrate django_celery_results

然后在settings.py里面设置restult的依赖为django中设置的数据库

CELERY_RESULT_BACKEND = 'django-db'
# 如果是基于缓存的
CELERY_CACHE_BACKEND = 'django-cache'

Broker

在settings.py里面配置Broker消息队列的接收器,这里使用redis。

CELERY_BROKER_URL = 'redis://localhost:6379/3'

#: Only add pickle to this list if your broker is secured
#: from unwanted access (see userguide/security.html)
CELERY_ACCEPT_CONTENT = ['json']
# 存储到数据库中
CELERY_RESULT_BACKEND = 'django-db'
CELERY_TASK_SERIALIZER = 'json'

定时任务

首先是在tasks.py中定义任务,然后在settings.py中配置运行策略

schedule

app.conf.beat_schedule = {
    'add-every-30-seconds': {
        'task': 'tasks.add',

        'schedule': 30.0,
        'args': (16, 16)
    },
}
app.conf.timezone = 'UTC'

有几个参数:

  • task:需要执行的task任务名字(函数名)
  • schedule:运行频率
  • args:运行函数时传入的参数,可以是列表或元组
  • kwargs:键值对类型的参数

crontab

也可以使用crontab来定时执行任务,可以具体到每天每周每月等

from celery.schedules import crontab

app.conf.beat_schedule = {
    # Executes every Monday morning at 7:30 a.m.
    'add-every-monday-morning': {
        'task': 'tasks.add',
        'schedule': crontab(hour=7, minute=30, day_of_week=1),
        'args': (16, 16),
    },
}

例如:

Example Meaning
crontab() 每分钟执行一次
crontab(minute=0, hour=0) 没天凌晨执行一次
crontab(minute=0, hour='*/3') 没三个小时执行一次
crontab(day_of_week='sunday') 每周日执行一次
crontab(0, 0,day_of_month='2') 每月第二天执行一次
crontab(0, 0, month_of_year='*/3') 每季度第一天执行一次