四十三、celery框架入门

Celery是一个基于Python的分布式任务队列,用于异步任务处理。它涉及的主要组件包括应用、broker(如Redis)、backend(存储任务结果)和worker。文章介绍了Celery的安装、配置,以及如何在Django项目中使用,并提供了使用Redis作为消息队列的示例。
摘要由CSDN通过智能技术生成

一、简介

Celery是使用python编写的分布式任务调度框架。

它有几个主要的概念:

(1)celery应用

用户编写的代码脚本,用来定义要执行的任务,然后通过broker将任务发送到消息队列中

(2)broker

  • 代理,通过消息队列在客户端和worker之间进行协调。

  • celery本身并不包含消息队列,它支持以下消息队列(RabbitMQ、Redis、Amazon SQS、Zookeeper)

  • 更多关于Broker见官方文档

(3) backend

  • 数据库,用来存储任务返回的结果。

(4)worker

  • 工人,用来执行broker分派的任务

  • 任务,定义的需要执行的任务

 

二、版本要求

Celery5.1 要求:

python(3.6,3.7,3.8)

Celery 是一个资金最少的项目,所以我们不支持 Microsoft Windows。

更多更详细的版本要求见官方文档 

三、安装

使用pip安装:

pip install -U Celery

(1)捆绑包

Celery还定义了一组包,用于安装Celery和给定的依赖项。

可以在pip命令中实现中括号来指定这些依赖项。

pip install -U Celery

pip install "celery[redis]"

具体支持的依赖包见官方文档 

四、简单使用

(1)选择一个broker

使用celery首先需要选择一个消息队列。安装任意你熟悉的前面提到的celery支持的消息队列。

安装redis容器

因为redis默认没有密码,使用云服务器部署redis容器时需要设置密码。新建配置文件 /root/redis.conf 编写如下

配置:

requirepass pythonvip

输入docker images命令,查看redis:alpine镜像是否下载。

没有的话先下载 redis:alpine镜像

docker pull redis:alpine

然后运行如下命令创建容器:

docker run -d -p 9000:6379 -v /root/redis.conf:/usr/local/etc/redis/redis.conf --name
myredis redis:alpine redis-server /usr/local/etc/redis/redis.conf

docker ps命令查看redis容器是否启动。 

redis的连接url格式如下:

redis://:password@hostname:port/db_number

进入容器: 

[root@hecs-394374 /]# docker exec -it myredis /bin/sh

 keys * 看所有的key

(error) NOAUTH Authentication required.  说明你还没有密码,

你得登录,所以咱先退出一下,因为我们没有用户名,只有密码。可以 -a 密码

redis-cli -a pythonvip  

 

 

(2)编写一个celery应用

首先我们需要编写一个celery应用,它用来创建任务和管理wokers,它要能够被其他的模块导入。

创建一个 tasks.py 文件:

from celery import Celery
app = Celery('tasks', broker='redis://localhost:6379/0')
@app.task
def add(x, y):
	return x + y

第一个参数 tasks 是当前模块的名称,它可以省略,建议以当前模块名为名称。

第二个关键字参数 broker='redis://localhost:6379/0' 指定我们使用redis作为消息队列,并指定连接地址。

 

(3)运行celery的worker服务

cd到ce.py所在目录,然后运行下面的命令来启动worker服务

TODO:记得去云服务器安全组中开放与redis的映射端口相对应的端口

celery -A tasks worker --loglevel=INFO

windows下的折中办法(学习调试,千万不要用在生产环境下)

1. 安装 eventlet 库

pip install eventlet

2. 运行worker是加上-P参数

celery -A tasks worker --loglevel=INFO -P eventlet

(4)调用任务

>>> from tasks import add
>>> add.delay(4,4)

通过调用任务的 delay 来执行对应的任务。celery会把执行命令发送到broker,broker再将消息发送给worker服务 来执行,如果一切正常你将会在worker服务的日志中看到接收任务和执行任务的日志。

(5)保存结果

如果你想要跟踪任务的状态以及保存任务的返回结果,celery需要把它发送到某个地方。celery提供多种结果后端。
我们这里以reids为例,修改 tasks.py 中的代码,添加一个redis后端。

# 创建应用
app = Celery('celery_project', broker='redis://:pythonvip@121.36.45.254:9000/7',
             backend='redis://:pythonvip@121.36.45.254:9000/8',
             include=['celery_project.tasks']  # 指定任务路径
             )

# 配置
app.conf.update(
    result_expires=3600  # 结果过期时间
)

更多结果后端见官方文档
重新启动worker服务,重新打开python解释器

>>> from tasks import add

>>> result = add.delay(4,4)

ready() 方法返回任务是否执行完成:

>>> result.ready()

False

还可以等待结果完成,但很少使用这种方法,因为它将异步调用转换为同步调用

>>> result.get(timeout=1)

8

五、在应用中使用celery

(1)创建项目

项目结构:

proj/__init__.py

/celery.py

/tasks.py

 


 proj/celery.py:

# 定义应用
from celery import Celery

app = Celery('celery_proj',
             broker='redis://localhost:6379/0',
             backend='redis://localhost:6379/1',
             include=['celery_proj.tasks'])  # 告诉我们的任务在哪里
# 配置
app.conf.update(
    result_expires=3600,  # 结果过期时间
)

在这个模块中我们创建了一个 Celery 模块。要在你的项目中使用celery只需要导入此实例。


proj/tasks.py

# 定义任务
from .celery import app


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


@app.task
def mul(x, y):
    return x * y


@app.task
def xsum(numbers):
    return sum(numbers)

(2)启动worker

celery -A celery_proj worker --loglevel=INFO

(3)调用任务

In [6]: from celery_proj.tasks import *

In [7]: res = mul.delay(3,4)

In [8]: res.get()
Out[8]: 12

In [9]: 

六、 在django中使用celery

要在你的django项目中使用celery,首先需要定义一个Celery的实例。

如果你又django项目如下:

 proj/
- manage.py
- proj/
    - __init__.py
    - settings.py
    - urls.py

那么推荐的方法是创建一个新的 proj/proj/celery.py 模块来定义芹菜实例:

file: proj/proj/celery.py

import os

from celery import Celery

# 为`celery`设置默认的django设置模块
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'proj.settings')

app = Celery('proj')

# 设置配置来源
app.config_from_object('django.conf:settings', namespace='CELERY')

# 加载所有的已注册django应用中的任务
app.autodiscover_tasks()

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

然后你需要在你的 proj/proj/__init__.py 模块中导入这个应用程序(就是lemontest的__init__文件中)。这样就可以保证 Django 启动时加载应用程 序,以便于 @shared_task 装饰器的使用。

proj/proj/__init__.py :

from .celery import app as celery_app
__all__ = ('celery_app',)

请注意,此示例项目布局适用于较大的项目,对于简单的项目,可以使用包含定义应用程序和任务的单个模块。

接下来我们来解释一下 celery.py 中的代码,首先,我们设置 celery 命令行程序的环境变量DJANGO_SETTINGS_MODULE 的默认值:

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'lemontest.settings')

这一行的作用是加载当前django项目的环境设置,特别是当需要在异步任务中用到orm。它必须在创建应用程序实例 之前。

app = Celery('lemontest')

我们还添加了Django设置模块作为Celery的配置源。这意味着我们不必使用多个配置文件,而是直接在Django的配 置文件中配置Celery。

app.config_from_object('django.conf:settings', namespace='CELERY')

大写命名空间意味着所有 Celery配置项 必须以大写指定,并以 CELERY_ 开头,因此例如 broker_url 设置变为 CELERY_BROKER_URL 。

例如,Django项目的配置文件可能包括:

settings.py

CELERY_TIMEZONE = "Asia/Shanghai"
CELERY_TASK_TRACK_STARTED = True
CELERY_TASK_TIME_LIMIT = 30*60

配置详细文档 

接下来,可重用应用程序的常见做法是在单独的 tasks.py 模块中定义所有任务, Celery 有一种方法可以自动发现 这些模块:

app.autodiscover_tasks()

使用上面的行,Celery 将按照 tasks.py 约定自动从所有已安装的应用程序中发现任务:

- app1/
    - tasks.py
    - models.py
- app2/
    - tasks.py
    - models.py

这样就不必手动将各个模块添加到 CELERY_IMPORTS 设置中。

七、使用@shared_task 装饰器

我们编写的任务可能会存在于可重用的应用程序中,而可重用的应用程序不能依赖与项目本身,因此无法直接导入 celery应用实例。
@shared_task 装饰器可以让我们无需任何具体的celery实例创建任务:
demoapp/tasks.py

# Create your tasks here

from demoapp.models import Widget

from celery import shared_task

@shared_task

def add(x, y):

return x + y

@shared_task

def mul(x, y):

return x * y

@shared_task

def xsum(numbers):

return sum(numbers)

@shared_task

def count_widgets():

return Widget.objects.count()

@shared_task

def rename_widget(widget_id, name):

w = Widget.objects.get(id=widget_id)

w.name = name

w.save()

八、项目中使用selery

(1)在lemontest包的__init__文件中:目的是项目启动的时候可以加载celery应用程序

from .celery import app as celery_app

__all__ = ('celery_app',)

(2)在lemontest包中新建celery文件

# -*- coding: utf-8 -*-
# time: 2022/11/15 21:08
# file: celery.py
# author: fade
import os
from celery import Celery
# 为`celery`设置默认的django设置模块
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'lemontest.settings')
app = Celery('lemontest')
# 设置配置来源为django项目的配置文件
app.config_from_object('django.conf:settings', namespace='CELERY')
# 加载所有的已注册django应用中的任务
app.autodiscover_tasks()

(3)在配置文件setting中添加配置:

# celery配置
CELERY_TIMEZONE = "Asia/Shanghai"
CELERY_WORKER_HIJACK_ROOT_LOGGER = False # 禁止celery自己的日志器
CELERY_BROKER_URL = 'redis://:pythonvip@121.36.27.254:9000/7'
# CELERY_BACKEND_URL = 'redis://:pythonvip@121.36.27.254:9000/8' # 需要保存结果,就配置

 (4)在tasks文件中导入shared_task

from celery import shared_task


@shared_task
def run_plan(plan_id, env_id, record_id):
    """执行测试计划"""
    ...
    ...

(5)若使用“应用中使用celery”的方法,就会要限制于代码依赖这个应用;代码强耦合;

(6)配置文件

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,  # 是否禁用已经存在的日志器
    # 日志格式化
    'formatters': {  # 日志信息显示的格式
        'verbose': {
            'format': '%(levelname)s %(asctime)s %(module)s %(lineno)d %(message)s'},
        'simple': {
            'format': '%(levelname)s %(module)s %(lineno)d %(message)s'},
    },
    # 日志过滤器
    'filters': {  # 对日志进行过滤
        'require_debug_true': {  # django在debug模式下才输出日志
            '()': 'django.utils.log.RequireDebugTrue', },
    },
    # 日志处理器
    'handlers': {  # 日志处理方法
        'console': {  # 向终端中输出日志
            'level': 'INFO',
            'filters': ['require_debug_true'],
            'class': 'logging.StreamHandler',
            'formatter': 'simple'},

        'file': {  # 向文件中输出日志
            'level': 'INFO',
            'class': 'logging.handlers.RotatingFileHandler',
            'filename': BASE_DIR / 'logs/lemontest.log',  # 日志文件的位置
            'maxBytes': 30 * 1024 * 1024,
            'backupCount': 10,
            'formatter': 'verbose'},
        # Celery日志处理器
        'celery': {
            'level': 'INFO',
            'class': 'logging.handlers.RotatingFileHandler',
            'filename': BASE_DIR / 'logs/celery.log',  # 日志文件的位置
            'maxBytes': 30 * 1024 * 1024,
            'backupCount': 10,
            'formatter': 'verbose'
        },
    },
    # 日志器
    'loggers': {  # 日志器
        'django': {  # 定义了一个名为django的日志器
            'handlers': ['console', 'file'],  # 可以同时向终端与文件中输出日志
            'propagate': True,  # 是否继续传递日志信息
            'level': 'INFO',  # 日志器接收的最低日志级别
        },
        'celery': {  # 定义了一个名为celery的日志器
            'handlers': ['console', 'celery'],  # 可以同时向终端与文件中输出日志
            'propagate': True,  # 是否继续传递日志信息
            'level': 'INFO',  # 日志器接收的最低日志级别
        },
    }
}

(7)视图中调用方法修改

run_plan.delay(plan_id=pk,env_id=env_id,record_id=record.id)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值