任务调度:Celery神器

任务调度:Celery神器

celery是一个强大的分布式任务队列,任务队列一般用于线程或计算机之间分配工作的一种机制。它的架构组成如下图:

image

  • 任务模块

包含异步任务与定时任务。异步任务通常在业务逻辑中被触发,并被发往任务队列;定时任务由celery beat进程周期性地将任务发往任务队列。

  • 消息中间件broker

broker,即为任务调度队列,接收任务生产者发来的任务消息,将任务存入队列。celery本身不提供任务队列,推荐使用RabbitMQ和Redis。

  • 任务执行单元worker

worker实时监控消息队列,获取调度的任务,并执行。

  • 任务结果存储backend

backend用于存储任务的执行结果,通消息中间件一样,存储可使用RabbitMQ,Redis,MongoDB等。

安装

$ pip install 'celery'

$ pip install eventlet ,如果window系统下安装的是celery4版本并运行报错,需要安装此包,后续启动worker时加上一个参数-P eventlet

请确保Redis以正确安装,并启动Redis服务。

创建Celery实例

# task.py
# -*- coding:utf-8 -*-

from celery import Celery
from celery.schedules import crontab
from datetime import timedelta

# 配置模块
class celeryconfig():
    # 指定消息中间件用 redis
    BROKER_URL = 'redis://127.0.0.1:6379/7'  
    # 指定存储用 redis
    CELERY_RESULT_BACKEND = 'redis://127.0.0.1:6379/8'  
    # 指定时区,默认是 UTC
    CELERY_TIMEZONE = 'Asia/Shanghai' 
	
    # 任务调度
    CELERYBEAT_SCHEDULE = {
        'add_every_10_seconds': {
            # 指定任务
            'task': 'tasks.add',
            # 每 10 秒执行一次
            'schedule': timedelta(seconds=10), 
            # 任务函数参数
            'args': (2, 32)
        }
    }

# 创建一个Celery实例app,名称为my_task
app = Celery('my_task')
# 通过 Celery 实例加载配置模块
app.config_from_object(celeryconfig)

# 你的任务
@app.task
def add(x, y):
    print("正在执行任务文件...")
    return x + y
  • 执行任务
$ celery -A tasks worker --loglevel=info -P eventlet
 -------------- celery@LAPTOP-P97PCAUQ v5.2.0 (dawn-chorus)
--- ***** -----
-- ******* ---- Windows-10-10.0.19041-SP0 2021-11-10 10:11:31
- *** --- * ---
- ** ---------- [config]
- ** ---------- .> app:         demo:0x1a7655f3b20
- ** ---------- .> transport:   redis://127.0.0.1:6379/7
- ** ---------- .> results:     redis://127.0.0.1:6379/8
- *** --- * --- .> concurrency: 8 (eventlet)
-- ******* ---- .> task events: OFF (enable -E to monitor tasks in this worker)
--- ***** -----
 -------------- [queues]
                .> celery           exchange=celery(direct) key=celery

其中 -A 指定了Celery实例的未知,实例在tasks.py文件中, Celery 会自动在该文件中寻找 Celery 对象实例;--loglevel 指定了日志级别,默认为warning;-P 解决Window系统下celery4版本运行的报错

  • 发布任务
$ celery -A tasks beat
celery beat v5.2.0 (dawn-chorus) is starting.
__    -    ... __   -        _
LocalTime -> 2021-11-10 10:18:17
Configuration ->
    . broker -> redis://127.0.0.1:6379/7
    . loader -> celery.loaders.app.AppLoader
    . scheduler -> celery.beat.PersistentScheduler
    . db -> celerybeat-schedule
    . logfile -> [stderr]@%WARNING
    . maxinterval -> 5.00 minutes (300s)

上述执行任务与发布任务分别在tasks.py文件目录下执行,当启动执行任务没有报错,再执行发布任务,此时在切换控制台窗口到worker的启动窗口,会看到执行任务的日志信息

[2021-11-10 10:19:11,128: INFO/MainProcess] Task tasks.add[6edc02df-3055-41f0-91ce-bb7e8f2aeb28] received
[2021-11-10 10:19:11,129: WARNING/MainProcess] 正在执行任务文件...
[2021-11-10 10:19:11,131: INFO/MainProcess] pidbox: Connected to redis://127.0.0.1:6379/7.

定时任务

Celery除了可以执行异步任务,也支持执行周期性任务(Periodic Tasks),或者说是定时任务, Celery Beat 进程通过读取配置文件的内容,周期性地将定时任务发往任务队列。

再来看一个定时任务例子,项目结构如下:

celery_demo                    # 项目根目录
    ├── celery_app             # 存放 celery 相关文件
    │   ├── __init__.py
    │   ├── celeryconfig.py    # 配置文件
    │   ├── task1.py           # 任务文件 1
    │   └── task2.py           # 任务文件 2

__init__.py 代码如下:

# -*- coding:utf-8 -*-

from celery import Celery

# 创建Celery实例
app = Celery('demo')
# 通过Celery实例加载配置模块
app.config_from_object('celery_app.celeryconfig')

celeryconfig.py 代码如下:

# -*- coding:utf-8 -*-

from datetime import timedelta
from celery.schedules import crontab

# Broker and Backend
BROKER_URL = 'redis://127.0.0.1:6379/7'  # 带密码 redis://:passwd@127.0.0.1:6379/7
CELERY_RESULT_BACKEND = 'redis://127.0.0.1:6379/8'

# 指定时区,不指定默认为 'UTC'
CELERY_TIMEZONE = 'Asia/Shanghai'

# import
CELERY_IMPORTS = {
    'celery_app.task1',
    'celery_app.task2',
}

# schedules
CELERYBEAT_SCHEDULE = {
        'add_every_20_seconds': {
            'task': 'celery_app.task1.add',
            'schedule': timedelta(seconds=20),  	# 每 20 秒执行一次
            'args': (2, 3)                      	# 任务函数参数
        },
        'multiply_at_some_time': {
            'task': 'celery_app.task2.multiply',     
            'schedule': crontab(hour=9, minute=50),   	# 每天早上 9 点 50 分执行一次
            'args': (2, 3)				# 任务函数参数
        }
    }

task1.py 代码如下:

# -*- coding:utf-8 -*-
from celery_app import app

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

task2.py 代码如下:

# -*- coding:utf-8 -*-
from celery_app import app

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

在项目的根目录下执行下面命令:

# 执行任务(窗口1)
celery_demo $ celery -A celery_app worker --loglevel=info -P eventlet
# 发布任务(窗口2)
celery_demo $ celery -A celery_app beat
  • 结尾

在生产环境中,我们通常会使用 Supervisor 来控制 Celery Worker 进程。

  • 相关资料

官⽅⼿册:https://docs.celeryproject.org/en/stable/
中⽂翻译:https://www.celerycn.io
Github:https://github.com/celery/celery

  • 参考资料:

【异步任务神器 Celery 简明笔记】:https://fast.v2ex.com/t/327228

posted @ 2021-11-10 11:07  Rosaany  阅读(531)  评论(1)    收藏  举报