celery介绍与python使用celery

celery介绍

Celery 官网:http://www.celeryproject.org/

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

Celery是一个简单、灵活且可靠的,处理大量消息的分布式系统,专注于实时处理的异步任务队列,同时也支持任务调度

Celery异步任务框架

  1. 可以不依赖任何服务器,通过自身命令,启动服务
  2. celery服务为其他项目服务提供异步解决任务需求

注:会有两个服务同时运行,一个是项目服务,一个是celery服务,项目服务将需要异步处理的任务交给celery服务,celery就会在需要时异步完成项目的需求

主要作用:

  1. 异步执行:解决耗时任务,将耗时操作任务提交给Celery去异步执行,比如发送短信/邮件、消息推送、音视频处理等等
  2. 延迟执行:解决延迟任务,比如5秒后发送短信等
  3. 定时执行:解决周期(周期)任务,比如每天数据统计

celery的架构

image

异步任务

普通使用

安装celery:

pip install celery

使用:app=Celery(‘任务名’, broker=’xxx’, backend=’xxx’)

第一步:实例化Celery对象,并注册任务。

from celery import Celery
# 存放任务的地方
broker = 'redis://127.0.0.1:6379/1'
# 存放任务结果的地方
backend = 'redis://127.0.0.1:6379/2'
# 实例化对象
app = Celery('main', broker=broker, backend=backend)

# 注册任务
@app.task
def add(a, b):  # 任务
    import time
    time.sleep(3)
    return a + b

第二步:提交任务到broker,也就是任务中间件上

# 导入add任务
res = add.delay(44, 55)  # 44 55为add函数的参数
print(res)

第三步:执行任务,启动worker

如果是windows,还需要安装:

pip install eventlet

然后终端切换到实例化Celery对象的py文件所在文件夹,执行

celery -A main worker -l info -P eventlet
celery -A 模块名(py文件名) worker -l info -P eventlet

注意:是模块名,不是任务名,并且执行时要在模块文件所在文件夹。

第四步:查看任务执行结果

终端打印

image

redis中

image

包结构使用

一般都是使用包来使用celery,因为这样可以让整体结构更加清晰。

包结构:

项目
├── celery_task -- 包名
├    ├── __init__.py
├    ├── celery.py -- celery对象存放,名字要为celery.py
├    └── tasks.py -- 任务存放注册
├── add_task.py -- 添加任务到队列中
└── get_result.py -- 获取任务执行结果

celery.py

from celery import Celery
# 任务存放处
broker = 'redis://127.0.0.1:6379/2'
# 任务执行结果存放处
backend = 'redis://127.0.0.1:6379/3'
# 任务路径
include = [
    'celery_task.tasks'
]

app = Celery('main', broker=broker, backend=backend, include=include)

tasks.py

from .celery import app

@app.task
def add(a, b):
    import time
    time.sleep(2)
    return a + b

add_task.py

from celery_task.tasks import add
# 提交任务返回的结果是一个任务id值
res = add.delay(5, 5)
print(res)

get_result.py

from celery_task.celery import app
from celery.result import AsyncResult

def get(task_id):
    asy = AsyncResult(id=task_id, app=app)
    if asy.successful():
        res = asy.get()
        print('任务执行结果:', res)
    elif asy.failed():
        print('任务失败')
    elif asy.status == 'PENDING':
        print('任务等待中被执行')
    elif asy.status == 'RETRY':
        print('任务异常后正在重试')
    elif asy.status == 'STARTED':
        print('任务已经开始被执行')

if __name__ == '__main__':
    # 任务id,提交任务时返回的结果
    task_id = 'bb52fd1a-43e6-4c36-852c-9b1c940a1ad7'
    get(task_id)

延迟任务

# 异步任务为delay(),延迟任务为apply_async()
res = add.apply_async(args=[5,7], countdown=5, retry=True)
"""
参数
    args:任务需要的参数
    countdown:几秒后执行
    retry:任务失败是否重试,默认为True
其他参数:
	eta:时间对象
"""
print(res)

定时任务

在使用包结构时,写在celery.py中:

from celery import Celery

broker = 'redis://127.0.0.1:6379/2'
backend = 'redis://127.0.0.1:6379/3'
include = [
    'celery_task.tasks'
]

app = Celery('main', broker=broker, backend=backend, include=include)
# 定时任务配置
from datetime import timedelta
from celery.schedules import crontab

app.conf.beat_schedule = {
    'add': {
        'task': 'celery_task.tasks.add',  # 任务路径
        'schedule': timedelta(seconds=3),  # 定时
        # 'schedule': crontab(hour=8, day_of_week=1),  # 每周一早八点
        'args': (100, 50),  # 任务参数
    }
}

除了启动worker

celery -A celery_task worker -l info -P eventlet

还要启动beat

celery -A celery_task beat -l info

django中使用celery

在操作celery时,无法导入django项目中的模块,所以需要以下操作。

第一步:将包(celery_task)放到项目根路径下

第二步:在 celery.py 中添加

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

第三步:django项目中引入任务执行

任务.delay()
任务.apply_async()

第四步:启动worker

celery -A celery_task worker -l info -P eventlet

第五步:启动beat

celery -A celery_task beat -l info

celery实现定时更新缓存

双写一致性

redis双写一致性指的是redis和数据库的数据要同时更新。

我们都知道把数据库的数据暂存于redis,之后取数据都去redis中取,这样做就可以减少时间消耗,但是会出现一个问题:数据库更新时,redis没有更新,所以取数据还是取得原来的值。

首先得了解数据库的数据是什么时候存到redis中的。

一般来说,前端发送请求,会先从redis中取,如果有值,则直接返回;如果没有值,就从数据库中取值并保存到redis中。

所以根据以上流程,有以下解决办法:

  1. 先更新数据库,再更新缓存
  2. 先删除缓存,再更新数据库
  3. 先更新数据库,再删除缓存 (这种比较多)
  4. 定时更新缓存(每隔5分钟更新一次缓存)

定时更新缓存

视图类:

class BannerView(GenericViewSet, ListModelMixin):
    queryset = models.Banner.objects.all()
    serializer_class = serializer.BannerSerializer

    def list(self, request, *args, **kwargs):
        banner_list = cache.get('banner_list')
        if banner_list:
            # redis中有值直接返回
            return Response(banner_list)
        else:
            # redis中没有值,获取数据再存入redis
            res = super(BannerView, self).list(request, *args, **kwargs)
            cache.set('banner_list', res.data)
            return res

celery.py

from celery import Celery
import os

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "luffyapi.settings.dev")
import django

django.setup()

broker = 'redis://127.0.0.1:6379/2'
backend = 'redis://127.0.0.1:6379/3'
include = [
    'celery_task.tasks'
]

app = Celery('main', broker=broker, backend=backend, include=include)
from datetime import timedelta

app.conf.beat_schedule = {
    'banner_update': {
        'task': 'celery_task.tasks.banner_update',  # 任务路径
        'schedule': timedelta(seconds=10),  # 定时
        'args': (),  # 任务参数
    }
}

任务:

@app.task
def banner_update():
    query_set = models.Banner.objects.all()
    ser = serializer.BannerSerializer(instance=query_set, many=True)
    cache.set('banner_list', ser.data)
    return True
posted @ 2022-07-14 17:42  Yume_Minami  阅读(408)  评论(0编辑  收藏  举报