分布式异步任务框架celery

Celery 官方文档中文版:http://docs.jinkan.org/docs/celery/

一、简介及安装

1.1、什么是celery?

celery是一个可以处理大量消息的分布式任务队列。

分布式决定了可以有多个 worker 的存在,队列表示其是异步操作,即存在一个产生任务提出需求的工头,和一群等着被分配工作的码农。

celery支持使用任务队列的方式在分布的机器、进程、线程上执行任务调度。

  •         任务队列就是一种在线程或机器间分发任务的机制。

 

1.2、它能做什么?

  1.  专注于实时处理的异步任务队列:异步任务
  2. 支持任务调度 :定时任务

1.3、使用场景

  • 异步任务:将耗时操作任务提交给Celery去异步执行,比如发送短信/邮件、消息推送、音视频处理等等
  • 定时任务:定时执行某件事情,比如每天数据统计

 1.4、celery架构

Celery的架构由三部分组成:

  1.        消息中间件(message broker)
  2.        任务执行单元(worker)
  3.        任务执行结果存储(task result store)组成。

消息中间件:

celery本身不提供消息服务,但是可以方便的和第三方提供的消息中间件集成 。包括,RabbitMQ, Redis等等。

Broker 担当一个中间人的角色。在工头提出任务的时候,把所有的任务放到 Broker 里面,在 Broker 的另外一头,一群码农等着取出一个个任务准备着手做。这种模式注定了整个系统会是个开环系统,工头对于码农们把任务做的怎样是不知情的。所以我们要引入 Backend 来保存每次任务的结果。这个 Backend 有点像我们的 Broker,也是存储任务的信息用的,只不过这里存的是那些任务的返回结果。我们可以选择只让错误执行的任务返回结果到 Backend,这样我们取回结果,便可以知道有多少任务执行失败了。backend其实就相当于一个数据库

任务执行单元:

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

任务结果存储:

 Task result store用来存储Worker执行的任务的结果,Celery支持以不同方式存储任务的结果,包括AMQP, redis等。

 1.5、安装配置

  • pip3 install celery
  • 消息中间件:RabbitMQ/Redis
  • app=Celery('任务名', backend='xxx', broker='xxx')

 二、使用

2.1、基本使用

先创建一个celery_app_task.py,任务文件(所有要执行的任务写在这里),文件名可以随便取

from celery import Celery

"""1、指定broker(消息中间件)和backend(结果存储)"""
# redis没有密码时
broker = 'redis://127.0.0.1:6379/0'
backend = 'redis://127.0.0.1:6379/1'
# redis有密码时
# backend='redis://:123456@127.0.0.1:6379/1'
# broker='redis://:123456@127.0.0.1:6379/2'

"""2、实例化产生一个Celery对象,一定要指定名字"""
app = Celery('test', broker=broker, backend=backend)

"""3、写任务,并在函数(add)上加装饰器app.task绑定任务"""
# 任务其实就是函数
# 需要用一个装饰器装饰,表示该任务是被celery管理的,且可用celery执行
@app.task
def add(x, y):
    import time
    time.sleep(3)
    return x+y

接下来创建一个提交任务的文件:add_task.py

import celery_app_task

# 正常同步执行任务
# res = celery_app_task.add(6, 6)
# print(res)  # 这边等待sleep时间后,直接可以打印结果

# 提交任务到消息队列中(只是提交到队列,并没有执行)
res = celery_app_task.add.delay(6, 6)
print(res)  # 立马提交任务,并返回了任务id:760473cf-9e62-4bae-b210-4cf381144ee5

接下来要启动worker去执行任务(celery_app_task是任务包的名字),需要安装eventlet模块:命令需要在项目路径执行

        linux:celery worker -A celery_app_task -l info

        windows:celery worker -A celery_app_task -l info -P eventlet

执行任务命令提交后,可以看到 提交的任务立即执行

如果我们想要定时执行任务,让它们在一段时间之后再执行,该怎么做呢?

import celery_app_task
from datetime import datetime, timedelta

# 定时任务:5s后执行add任务
"""第一种获取时间"""
'''
v1 = datetime(2020, 1, 1, 00, 00, 00)  # 可以固定设置一个时间执行任务
print(v1)  # 2020-01-01 00:00:00
print(v1.timestamp())  # 将上面的时间转换为时间戳
v2 = datetime.utcfromtimestamp(v1.timestamp())
print(v2)  # 2019-12-31 16:00:00
# 取出要执行任务的时间对象,调用apply_async方法,args是参数,eta是执行的时间
result = celery_app_task.add.apply_async(args=[1, 3], eta=v2)
print(result.id)
'''

"""第二种获取时间"""
v1 = datetime.now()  # 获取实时时间
# 默认用utc时间
utc_time = datetime.utcfromtimestamp(v1.timestamp())
# print(v2)  # 将时间戳转换为utc时间:2019-07-12 08:50:30

# 取5s后的时间对象
time_delay = timedelta(seconds=5)  # 可以设置延时几天,几分,几秒后执行
task_time = utc_time + time_delay

# 取出要执行任务的时间对象,调用apply_async方法,args是参数,eta是执行的时间
res = celery_app_task.add.apply_async(args=[4, 3], eta=task_time)
print(res.id)

 创建py文件:result.py,查看任务执行结果

from celery_app_task import app
from celery.result import AsyncResult

# 根据id拿到结果
async = AsyncResult(id='760473cf-9e62-4bae-b210-4cf381144ee5', app=app)

if async.successful():
    # 取出return的结果
    res = async.get()
    print(res)  # 12
    # res.forget()  # 删除结果
elif async.failed():
    print('执行失败')
elif async.status == 'PENDING':
    print('任务等待中被执行')
elif async.status == 'RETRY':
    print('任务异常后正在重试')
elif async.status == 'STARTED':
    print('任务已经开始被执行')

2.2、celery的多任务执行

项目结构:

pro_cel  # 项目
    ├── celery_task    # celery相关文件夹
    │   ├── celery.py   # celery连接和配置相关文件,必须叫这个名字
    │   └── tasks1.py    #  所有任务函数
    │   └── tasks2.py    #  所有任务函数
    ├── check_result.py # 检查结果
    └── send_task.py    # 触发任务

windows启动执行任务:celery worker -A celery_task -l info -P eventlet

 先来创建celery.py配置文件,一定要叫这个名字,因为启动的时候,直接写到文件夹一层,自动去找这个名字

下面的场景是每天的什么时候,或者每月的某个时候定时做某些工作。

比如:秒杀推送信息,每天某个点同步redis的数据到数据库等

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

app = Celery('celery_demo',
             broker='redis://127.0.0.1:6379/1',
             backend='redis://127.0.0.1:6379/2',
             # 包含以下两个任务文件,去相应的py文件中找任务,对多个任务做分类
             include=['celery_task.user_task',
                      'celery_task.order_task'
                      ])

# 时区
# cel.conf.timezone = 'Asia/Shanghai'
# 是否使用UTC
# cel.conf.enable_utc = False
"""配置文件app.conf"""
app.conf.beat_schedule = {
    # 名字随意命名
    'add-every-10-seconds': {
        # 执行tasks1下的test_celery函数
        'task': 'celery_task.order_task.order_add',
        # 每隔2秒执行一次
        # 'schedule': 1.0,
        # 'schedule': crontab(minute="*/1"),
        'schedule': timedelta(seconds=2),
        # 传递参数
        'args': (5, 6)
    },
    """crontab定时"""
    # 'add-every-12-seconds': {
    #     'task': 'celery_task.order_task.order_add',
    #     # 每年4月11号,8点42分执行
    #     'schedule': crontab(minute=42, hour=8, day_of_month=11, month_of_year=4),
    #     # 'schedule': crontab(minute=42, hour=8, day_of_month=11, month_of_year=4),
    #     'args': (16, 16)
    # },
}

然后准备一下其他两个任务文件,这个根据自己的项目需求来看

"""user_task.py"""

import time
from celery_task.celery import app

@app.task
def user_add(x, y):
    time.sleep(3)
    return x+y
"""order_task.py"""

import time
from celery_task.celery import app

@app.task
def order_add(x, y):
    time.sleep(3)
    return x+y

然后提交任务的文件send_task.py

from celery_task.order_task import order_add
from celery_task.user_task import user_add

res = user_add.delay(10, 50)
print(res)
ret = order_add.delay(5, 10)
print(ret)

我上面的配置想要让程序每隔2秒提交一次任务,或者每年每天定时执行哪个任务,那么就需要先启动一个beat

 celery beat -A celery_task -l info

然后启动任务执行(windows下,linux下不用-P后的参数):

celery worker -A celery_task -l info -P eventlet

最后查询结果的文件:check_result.py

from celery.result import AsyncResult
from celery_task.celery import app
async = AsyncResult(id="44648a25-42f7-4f00-acc2-28e1335b8a7b", app=app)

if async.successful():
    # 取出它return的值
    result = async.get()
    print(result)
    # result.forget() # 将结果删除,执行完成,结果不会自动删除
    # async.revoke(terminate=True)  # 无论现在是什么时候,都要终止
    # async.revoke(terminate=False) # 如果任务还没有开始执行呢,那么就可以终
elif async.failed():
    print('执行失败')
elif async.status == 'PENDING':
    print('任务等待中被执行')
elif async.status == 'RETRY':
    print('任务异常后正在重试')
elif async.status == 'STARTED':
    print('任务已经开始被执行')

三、django中使用

django中虽然提供了django-celery,但是对于django和celery的版本的匹配度要求很高,所以我们还是使用原生的celery。

 首先我们把之前的celery_task文件夹可以直接拷贝到我们项目的目录下使用,最主要还是那个celery.py配置,其他的任务根据项目需求来就行了。这里做测试,我还是拿之前的user_task.py和order_task.py文件

接下来我们去写视图

from django.shortcuts import HttpResponse
from celery_task.user_task import user_add
from celery.result import AsyncResult
from celery_task.celery import app


def index(request):
    # 提交任务,返回id
    res = user_add.delay(8, 8)
    return HttpResponse(res.id)


def check_result(request):
    res = request.GET.get('id')
    async = AsyncResult(id=res, app=app)
    if async.successful():
        # 取出结果
        result = async.get()
        print(result)
        return HttpResponse(result)

配好路由,就可以访问了,别忘了执行前面说的命令执行任务啊:celery worker -A celery_task -l info -P eventlet

然后访问check,拿到结果:


 

注意, 任务中需要调用models查询数据库时,一定要导入,就想manage.py的那里一样,不然无法使用orm查询数据。

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "untitled15.settings")
import django
django.setup()

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值