celery快速使用

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

介绍:

Celery is a project with minimal funding, so we don’t support Microsoft Windows. Please don’t open any issues related to that platform

celery是独立的服务

1.可以不依赖任何服务器,通过自身命令,启动服务
2.celery服务为为其他项目服务提供异步解决任务需求的
注:会有两个服务同时运行,一个是项目服务,一个是celery服务,项目服务将需要异步处理的任务交给celery服务,celery就会在需要时异步完成项目的需求

人是一个独立运行的服务(django) | 医院也是一个独立运行的服务(celery)
正常情况下,人可以完成所有健康情况的动作,不需要医院的参与;但当人生病时,就会被医院接收,解决人生病问题
人生病的处理方案交给医院来解决,所有人不生病时,医院独立运行,人生病时,医院就来解决人生病的需求

安装pip3 install celery

使用步骤

1.写个main.py

实例化得到app对象,写函数,任务,注册成celery的任务

from celery import Celery
import time

# backend='redis://:123456@127.0.0.1:6379/1' # 后续应该加上密码
backend = 'redis://127.0.0.1:6379/1'
broker = 'redis://127.0.0.1:6379/0'
app = Celery(__name__, backend=backend, broker=broker)

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

2.在别的程序中

提交任务--->提交到broker中去了

from main import add

# 同步任务
print(666)
res = add(3, 5)
print(res)

# 异步任务
res = add.delay(3,5)
print(res)

3.启动worker

从broker中取任务执行,执行完放到backend中

windows需要安装pip3 install eventlet

windows
	celery worker -A main -l info -P eventlet  # 4.x及之前用这个 
	celery -A main worker -l info -P eventlet  # 5.x及之后用这个
linux,mac 
	celery worker -A main -l info
	celery -A main worker -l info

4.在backend中查看执行的结果

1.通过redis可视化软件直接查看

2.通过代码查看
from main import app
from celery.result import AsyncResult
id = '7bef14a0-f83e-4901-b4d8-e47aaac09b39'

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

celery包结构

写一个celery的包,以后再任意项目中想用,把包copy进去,导入使用即可

1.包结构

project
    ├── celery_task  	# celery包
    │   ├── __init__.py # 包文件
    │   ├── celery.py   # celery连接和配置相关文件,且名字必须叫celery.py
    │   ├── user_task.py  # user相关所有任务函数
    │   └── home_task.py  # home想关所有任务函数
    ├── add_task.py  	   # 添加任务
    └── get_result.py     # 获取结果

2.使用步骤

1.新建包:celery_task
2.在包新建名为一个:celery.py
3.在里面写app的初始化
4.在包里新建user_task.py 编写用户相关任务
5.在包里新建home_task.py 编写首页相关任务
6.其他程序中,提交任务
7.启动worker --->worker可以先启动,在提交任务之前--->包所在的目录下执行启动命令
	celery -A celery_task worker -l info -P eventlet
8.就可以查看任务的执行结果了
celery_task/celery.py

不要忘了include注册

from celery import Celery

backend = 'redis://127.0.0.1:6379/1'
broker = 'redis://127.0.0.1:6379/0'

app = Celery(__name__, broker=broker, backend=backend, include=['celery_task.home_task', 'celery_task.user_task'])
celery_task/home_task.py
from .celery import app
import time

@app.task
def add(a, b):
    time.sleep(3)
    print('计算结果是:%s' % (a + b))
    return a + b
celery_task/user_task.py
import time
from .celery import app

@app.task
def send_sms(mobile, code):
    time.sleep(1)
    print('给%s发送成功,验证码是%s' % (mobile, code))
    return True
add.py
from celery_task.user_task import send_sms
# 提交了一个发送短信异步任务
res =send_sms.delay('191****1000','9999')
print(res)  # 返回uuid随机字符串 672237ce-c941-415e-9145-f31f90b94627

# 任务执行,要启动worker
# 查看任务执行的结果
get_result.py
# 查询执行完的结果
from celery_task.celery import app
from celery.result import AsyncResult

id = '672237ce-c941-415e-9145-f31f90b94627'
if __name__ == '__main__':
    res = AsyncResult(id=id, app=app)
    if res.successful():
        result = res.get()
        print(result)
    elif res.failed():
        print('任务失败')
    elif res.status == 'PENDING':
        print('任务等待中被执行')
    elif res.status == 'RETRY':
        print('任务异常后正在重试')
    elif res.status == 'STARTED':
        print('任务已经开始被执行')

celery异步任务,延迟任务,定时任务

1.异步任务(delay)

任务函数.delay(参数,参数)

from main import add

res = add.delay(3,5)
print(res)

2.延迟任务(apply_async)

任务函数.apply_async(args=[参数,参数],eta=时间对象(默认utc时间))

from main import add
from datetime import datetime, timedelta

eta = datetime.utcnow() + timedelta(seconds=10)  # utc时间
# local_eta = datetime.now() + timedelta(seconds=10)  # 当地时间
res = add.apply_async(args=(155, 20,), eta=eta)
print(res)

3.定时任务

时区 app.conf 所有的配置字典 默认的

from celery import Celery

backend = 'redis://127.0.0.1:6379/1'
broker = 'redis://127.0.0.1:6379/0'
app = Celery(__name__, broker=broker, backend=backend, include=['celery_task.home_task', 'celery_task.user_task'])

app.conf.timezone = 'Asia/Shanghai'  # 默认utc时间 修改成上海时间
app.conf.enable_utc = False  # 是否使用UTC时间
定时任务配置

1.app的配置文件中配置

from datetime import timedelta
from celery.schedules import crontab

app.conf.beat_schedule = {
    'send_sms_task': {
        'task': 'celery_task.user_task.send_sms',  # 指定是哪一个任务
        'schedule': timedelta(seconds=5),  # 时间对象 每隔5秒发送一次
        'args': ('191****1000', '6666'),   # 传入参数
    },
    'add_task': {
        'task': 'celery_task.home_task.add',
        'schedule': crontab(hour=12, minute=10, day_of_week=3),  # 每周一早八点
        'args': (10, 20),
    }
}

2.启动worker : 干活的人

celery -A celery_task worker -l info -P eventlet

3.启动beat :提交任务的人

celery -A celery_task beat -l info

django中使用celery

1.把写好的包复制到项目路径下

image

2.在包的celery.py中加入以下代码

import os
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'luffy_api.settings.dev')
import django
django.setup()

3.在django视图类中导入,提交任务

from celery_task.user_task import send_sms

def index(request):
    mobile = request.GET.get('mobile')
    res = send_sms(mobile, '6666')
    print(res)
    return JsonResponse('发送了')

4.启动worker,beat

celery -A celery_task worker -l info -P eventlet
celery -A celery_task beat -l info

秒杀逻辑

前端

秒杀按钮

事件: 向后端秒杀接口发送请求,发送完立马起了一个定时任务,每隔5s,向后端查看一下是否秒杀成功,如果秒杀没成功,定时任务继续执行,如果秒杀成功了,清空定时任务,弹窗告诉他

handleClick() {
      this.$axios.get(this.$settings.BASE_URL + 'userinfo/seckill/').then(res => {
        if (res.data.code == 100) {
          let task_id = res.data.id
          this.$message({
            message: res.data.msg,
            type: 'error'
          });
          // 起个定时任务,每隔5s向后端查询一下是否秒杀成功
          let t = setInterval(() => {
            this.$axios.get(this.$settings.BASE_URL + 'userinfo/get_result/?id=' + task_id).then(
                res => {
                  if (res.data.code == 100 || res.data.code == 101) {  //秒杀结束了,要么成功,要么失败了
                    alert(res.data.msg)
                    // 销毁掉定时任务
                    clearInterval(t)
                  } else if (res.data.code == 102) {
                    //什么事都不干
                  }
                }
            )
          }, 5000)


        }
      })
    }

后端

1.秒杀接口
提交秒杀任务

def seckill(request):
    res = seckill_task.delay()
    return JsonResponse({'code': 100, 'msg': '正在排队', 'id': str(res)})

2.查询是否秒杀成功的接口
根据用户传入的id,查询任务是否成功

 def get_result(request):
        task_id = request.GET.get('id')
        res = AsyncResult(id=task_id, app=app)
        if res.successful():
            result = res.get()
            return JsonResponse({'code': 100, 'msg': str(result)})
        elif res.failed():
            print('任务失败')
            return JsonResponse({'code': 101, 'msg': '秒杀失败'})
        elif res.status == 'PENDING':
            print('任务等待中被执行')
            return JsonResponse({'code': 102, 'msg': '还在排队'})

双写一致性

首页轮播图接口,加缓存

提交了接口的响应速度,提高并发量

接口上加缓存

from rest_framework.viewsets import GenericViewSet
from .serializer import BannerSerializer
from .models import Banner
from utils.view import CommonListModelMixin
from django.conf import settings
from django.core.cache import cache
from utils.response import ResponseApi

class BannerView(GenericViewSet, CommonListModelMixin):
    queryset = Banner.objects.filter(is_delete=False, is_show=True).order_by('orders')[:settings.BANNER_COUNT]
    serializer_class = BannerSerializer

    def list(self, request, *args, **kwargs):  # 群查
        banner_list = cache.get('banner_list')  # 缓存有,走缓存,没有就走数据库
        if not banner_list:
            response = super().list(request, *args, *kwargs)
            result = response.data.get('result')
            cache.set('banner_list', result)  # 缓存不设置过期时间
            print('走了数据库')
            return response
        return ResponseApi(result=banner_list)

celery定时任务实现双写一致性

celery_task/home_task.py
from .celery import app
from django.conf import settings
from home.models import Banner
from home.serializer import BannerSerializer
from django.core.cache import cache

@app.task
def update_banner():
    # 更新缓存
    # 查询出现在轮播图的数据
    queryset = Banner.objects.all().filter(is_delete=False, is_show=True).order_by('orders')[:settings.BANNER_COUNT]
    ser = BannerSerializer(instance=queryset, many=True)
    # ser 中得图片,没有前面地址
    for item in ser.data:
        item['image'] = settings.HOST_URL + item['image']
    cache.set('banner_list', ser.data)
    return True

celery_task/celery.py

from celery import Celery
from datetime import timedelta
import os
import django

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'luffy_api.settings.dev')
django.setup()

backend = 'redis://127.0.0.1:6379/1'
broker = 'redis://127.0.0.1:6379/0'
app = Celery(__name__, broker=broker, backend=backend, include=['celery_task.home_task', 'celery_task.user_task'])

app.conf.beat_schedule = {
    'update_banner': {
        'task': 'celery_task.home_task.update_banner',
        'schedule': timedelta(seconds=50),
        'args': (),
    }
}
 posted on 2022-11-16 22:40  Joker_Ly  阅读(145)  评论(0)    收藏  举报