python笔记(celery框架初识)

一、Celery的定义

  1. Celery(芹菜)是一个简单、灵活且可靠的,处理大量消息的分布式系统,并且提供维护这样一个系统的必需工具。

  2. 我比较喜欢的一点是:Celery支持使用任务队列的方式在分布的机器、进程、线程上执行任务调度。然后我接着去理解什么是任务队列。

  3. 任务队列

    任务队列是一种在线程或机器间分发任务的机制。
    
  4. 消息队列

    消息队列的输入是工作的一个单元,称为任务,独立的职程(Worker)进程持续监视队列中是否有需要处理的新任务。

    Celery 用消息通信,通常使用中间人(Broker)在客户端和职程间斡旋。这个过程从客户端向队列添加消息开始,之后中间人把消息派送给职程,职程对消息进行处理。如下图所示:

    在这里插入图片描述

    Celery 系统可包含多个职程和中间人,以此获得高可用性和横向扩展能力。

  5. Celery的架构

    Celery的架构由三部分组成,消息中间件(message broker),任务执行单元(worker)和任务执行结果存储(task result store)组成。

    消息中间件

    Celery本身不提供消息服务,但是可以方便的和第三方提供的消息中间件集成,包括,RabbitMQ,Redis,MongoDB等,这里我先去了解RabbitMQ,Redis。
    

    任务执行单元

    Worker是Celery提供的任务执行的单元,worker并发的运行在分布式的系统节点中
    

    任务结果存储

    Task result store用来存储Worker执行的任务的结果,Celery支持以不同方式存储任务的结果,包括Redis,MongoDB,Django ORM,AMQP等,这里我先不去看它是如何存储的,就先选用Redis来存储任务执行结果。
    

二、简单操作

cereryconfig.py

BROKER_URL = "redis://:@127.0.0.1/1"
CELERY_RESULT_BACKEND = "redis://:@127.0.0.1:6379/2"
CELERY_TASK_SERIALIZER = 'json'
CELERY_RESULT_SERIALIZER = 'json'
CELERY_TASK_RESULT_EXPIRES = 60 * 60 * 24  # 任务过期时间
CELERY_ACCEPT_CONTENT = ["json"]

"""
CELERY_DEFAULT_QUEUE   # 默认队列
CELERY_BROKER_URL # Broker 地址
CELERY_RESULT_BACKEND # 结果存储地址
CELERY_TASK_SERIALIZER # 任务序列化方式
CELERY_RESULT_SERIALIZER # 任务执行结果序列化方式
CELERY_TASK_RESULT_EXPIRES # 	任务过期时间
CELERY_ACCEPT_CONTENT # 指定任务接受的内容类型(序列化)

"""

celerys.py

from celery import Celery


"""
1、Celery第一个参数是给其设定一个名字, 第二参数我们设定一个中间人broker, 在这里我们使用Redis作为中间人。
my_task函数是我们编写的一个任务函数, 通过加上装饰器app.task, 将其注册到broker的队列中

2、如果我们想跟踪任务的状态,Celery需要将结果保存到某个地方。
有几种保存的方案可选:SQLAlchemy、Django ORM、Memcached、 Redis、RPC (RabbitMQ/AMQP)。
配置backend参数

"""
app = Celery("demo")

# 从单独的配置模块中加载配置
app.config_from_object('celeryconfig')

# 创建任务函数
@app.task  # 将其注册到broker的队列中。
def my_task(a, b):
    print("任务在执行。。。。")
    return a + b

tasks.py

from celerys import my_task
"""
任务加入到broker队列中,以便刚才我们创建的celery workder服务器能够从队列中取出任务并执行。
如何将任务函数加入到队列中,可使用delay()。

解决错误:ask handler raised error: ValueError('not enough values to unpack (expected 3, got 0)',)
Traceback (most recent call last):

pip install eventlet
celery -A <mymodule> worker -l info -P eventlet
"""
ret = my_task.delay(1, 2)
ret.failed()  # 错误结果
print(ret.result)  # 获取返回值
"""
返回值:
    <AsyncResult: 2c0f6100-d499-44a0-aa0d-57ebec7fdd4c>:这个对象可以用来检查任务的状态或者获得任务的返回值。
    
"""

三、celery基本结构

在这里插入图片描述
cereryconfig.py

from celery.beat import crontab


BROKER_URL = "redis://:@127.0.0.1/1"
CELERY_RESULT_BACKEND = "redis://:@127.0.0.1:6379/2"


# Routing
CELERY_ROUTES = ({
    'celery_proj.tasks.my_task1': {'queue': 'queue1'},
    'celery_proj.tasks.my_task2': {'queue': 'queue1'},
    'celery_proj.tasks.my_task3': {'queue': 'queue2'},

})


# 配置周期性任务,或者定时任务,5秒执行一次(celery beat)
BEAT_SCHEDULE = {
    'every-5-seconds':
        {
            'task': 'celery_proj.tasks.my_task1',
            'schedule': 5.0,
            # 'args': (16, 16),
        }
}

# 如果我们想指定在某天某时某分某秒执行某个任务,可以执行cron任务, 增加配置信息如下:

beat_schedule = {
    'every-5-minute':
        {
            'task': 'celery_proj.tasks.period_task',
            'schedule': 5.0,
            'args': (16, 16),  # 参数函数
        },
    'add-every-monday-morning': {
        'task': 'celery_proj.tasks.period_task',
        'schedule': crontab(hour=7, minute=30, day_of_week=1),
        'args': (16, 16),
    },

}

"""
启动队列:
celery -A celery_proj.celerys worker -l info -Q queue1 -P eventlet
celery -A proj worker --loglevel=info -Q queue1,queue2 直接开启两个队列
"""

celerys.py

from celery import Celery

# 创建celery实例
app = Celery('demo')
app.config_from_object('celery_proj.celeryconfig')

# 自动搜索任务
app.autodiscover_tasks(['celery_proj'])

tasks.py

from celery_proj.celerys import app as celery_app


# 创建任务函数
@celery_app.task
def my_task1():
    print("任务函数(my_task1)正在执行....")


@celery_app.task
def my_task2():
    print("任务函数(my_task2)正在执行....")


@celery_app.task
def my_task3():
    print("任务函数(my_task3)正在执行....")


@celery_app.task
def my_task4(a, b):
    print("任务函数(my_task4)正在执行....")
    return a + b


@celery_app.task
def my_task5(a, b):
    print("任务函数(my_task5)正在执行....")
    return a + b


@celery_app.task
def my_task6(a, b):
    print("任务函数(my_task6)正在执行....")
    return a + b


@celery_app.task
def period_task(a, b):
    print(a + b)

test.py

from celery_proj.tasks import my_task1, my_task2, my_task3, my_task4, my_task5, my_task6
from celery import group
from celery import chain


# my_task1.delay()
"""
调用任务:
    可以使用apply_async()方法,该方法可让我们设置一些任务执行的参数,
    例如,任务多久之后才执行,任务被发送到那个队列中等等.
    my_task.apply_async((2, 2), queue='my_queue', countdown=10)
    任务my_task将会被发送到my_queue队列中,并且在发送10秒之后执行。
    如果我们直接执行任务函数,将会直接执行此函数在当前进程中,并不会向broker发送任何消息。
    无论是delay()还是apply_async()方式都会返回AsyncResult对象,
    方便跟踪任务执行状态,但需要我们配置result_backend.
    每一个被调用的任务都会被分配一个ID,我们叫Task ID.
    queue="queue1": 指定用哪个队列
"""
# my_task2.apply_async(queue="queue1", countdown=10)

"""
一个signature包装了一个参数和执行选项的单个任务调用。我们可将这个signature传递给函数。
我们将my_task1()任务包装称一个signature:10秒后执行
"""
# t3 = my_task3.signature(countdown=10)
# t3.delay()


"""
Primitives:
  这些primitives本身就是signature对象,因此它们可以以多种方式组合成复杂的工作流程。primitives如下:
  group: 一组任务并行执行,返回一组返回值,并可以按顺序检索返回值。
  chain: 任务一个一个执行,一个执行完将执行return结果传递给下一个任务函数.

"""
# 将多个signature放入同一组中
# my_group = group((my_task4.s(11, 12), my_task5.s(1, 12), my_task6.s(11, 2)))
# ret = my_group()  # 执行组任务
# print(ret.get())  # 输出每个任务结果

"""
将多个signature组成一个任务链
my_task4的运行结果将会传递给my_task5
my_task5的运行结果会传递给my_task6
"""
# my_chain = chain(my_task4.s(10, 10) | my_task5.s(10) | my_task6.s(10))
# ret = my_chain()  # 执行任务链
# print(ret.get())  # 输出最终结果


"""
Routing(要写配置文件)
  假如我们有两个worker,一个worker专门用来处理邮件发送任务和图像处理任务,一个worker专门用来处理文件上传任务。
  我们创建两个队列,一个专门用于存储邮件任务队列和图像处理,一个用来存储文件上传任务队列。
  Celery支持AMQP(Advanced Message Queue)所有的路由功能,我们也可以使用简单的路由设置将指定的任务发送到指定的队列中.

"""
# my_task1.apply_async(queue='queue1')
# my_task3.apply_async(queue='queue2')

"""
Periodic Tasks:(要写配置文件)
  celery beat是一个调度器,它可以周期内指定某个worker来执行某个任务。
  如果我们想周期执行某个任务需要增加beat_schedule配置信息.  
  不能在windows上运行
  启动woker处理周期性任务:celery -A proj worker --loglevel=info --beat
  
  celery需要保存上次任务运行的时间在数据文件中,文件在当前目录下名字叫celerybeat-schedule. beat需要访问此文件:
    celery -A proj beat -s /home/celery/var/run/celerybeat-schedule
"""

三、celery在Django中的应用

在这里插入图片描述

tasks.py

from django_celery.celerys import app
import time


# 加上app对象的task装饰器
# 此函数为任务函数
@app.task
def my_task():
    print("任务开始执行....")
    time.sleep(5)
    print("任务执行结束....")


# 用于定时执行的任务
@app.task
def interval_task():
    print("我每隔5秒钟时间执行一次....")

views.py

from django.shortcuts import render
from django.http import HttpResponse
from .tasks import my_task
# Create your views here.
"""
django-admin startproject celery_demo 创建Django项目
python manage.py startapp demo 创建App
python -m pip install --upgrade pip
"""


def index(request):
    # 将my_task任务加入到celery队列中
    # 如果my_task函数有参数,可通过delay()传递
    # 例如 my_task(a, b), my_task.delay(10, 20)
    my_task.delay()
    return HttpResponse("<h1>服务器返回响应内容!</h1>")

celery.py

from celery import Celery
from django.conf import settings
import os

# 为celery设置环境变量
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'django_celery.settings')

# 创建应用
app = Celery("app1")
# 配置应用
app.conf.update(
    # 配置broker, 这里我们用redis作为broker
    BROKER_URL='redis://:@127.0.0.1:6379/1',
    # 使用项目数据库存储任务执行结果
    CELERY_RESULT_BACKEND='django-db',
    # 配置定时器模块,定时器信息存储在数据库中
    CELERYBEAT_SCHEDULER='django_celery_beat.schedulers.DatabaseScheduler'
)
# 设置app自动加载任务
# 从已经安装的app中查找任务
app.autodiscover_tasks(settings.INSTALLED_APPS)


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

settings.py

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'app1.apps.App1Config',
    'django_celery_results',  # 注意此处应用名为下划线
    'django_celery_beat',  # 安装应用
]

urls.py

from django.conf.urls import url
from django.contrib import admin
from app1.views import index

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^index/', index),
]

猜你喜欢

转载自blog.csdn.net/qq_41433183/article/details/90732688