Loading

celery包结构、秒杀、celery异步任务,延迟任务,定时任务、双写一致性、django中使用celery

celery包结构

# 包结构,用的多,好处,写好了包,以后copy到任意项目中,都可以顺利使用

目录

celery_task    # 包
    __init__.py
    celery.py
    goods_task.py
    order_task.py
    user_task.py
#------其它项目:提交和查询结果----------
add_task.py
get_result.py

celery_task/celery.py

from celery import Celery
backend = 'redis://127.0.0.1:6379/1'  # 结果存储
broker = 'redis://127.0.0.1:6379/2'  # 消息中间件
app = Celery('main', backend=backend, broker=broker,
             include=['celery_task.goods_task', 'celery_task.order_task', 'celery_task.user_task'])

# include: 写的是字符串路径,celery_task包下的py文件名

celery_task/user_task.py

from .celery import  app
import time
@app.task
def send_sms(phone,code):
    # 调用发短信方法---》模拟
    time.sleep(2)
    return '%s的手机号,发送验证码:%s,已成功'%(phone,code)

add_task.py

from celery_task.user_task import send_sms

# 提交任务
res=send_sms.delay('18953675221','8888')  # 返回了一个随机字符串,任务id号
print(res)

get_result.py

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


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

秒杀逻辑

<template>
  <div>
    <button @click="handleClick">秒杀秒杀秒杀</button>
  </div>
</template>

<script>
export default {
  name: "seckill",
  data() {
    return {
      task_id: ''
    }
  },
  methods: {
    handleClick() {
      this.$axios.post('http://127.0.0.1:8000/api/v1/user/text/', {
        id: '999'
      }).then(res => {
        this.$message({
          message: res.data.msg,
          type: 'warning'
        });
        this.task_id = res.data.task_id
        let interval = setInterval(() => {
          this.$axios.get('http://127.0.0.1:8000/api/v1/user/text/?task_id=' + this.task_id).then(res => {
            console.log(res.data.code)
            if (res.data.code == 666) {
              clearInterval(interval);
              this.$message({
                message: res.data.msg,
                type: 'warning'
              });
            } else {
              this.$message({
                message: res.data.msg,
                type: 'warning'
              });
            }
          })
        }, 3000)
      })

    }
  }
}
</script>

<style scoped>

</style>

后端

views.py

class TextView(APIView):
    def post(self, request):
        shop_id = request.data.get('id')
        res = tasks.seckill.delay('id')
        return APIResponse(msg='您正在排队', task_id=str(res))
    
    # 获取状态信息
    def get(self, request):
        task_id = request.query_params.get('task_id')
        print(task_id)
        async1 = AsyncResult(id=task_id, app=app)
        if async1.successful():
            result = async1.get()
            return APIResponse(code=666, msg='秒杀成功')
        elif async1.status == 'PENDING':
            return APIResponse(msg='任务等待中被执行')
        elif async1.status == 'STARTED':
            return APIResponse(msg='任务已经开始被执行')

celery_task/celery

# 一、加载django配置环境
import os
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "luffyapi.settings.dev")
import django

django.setup()
# 二、加载celery配置环境
from celery import Celery

# brokercelery worker -A celery_task -l info -P eventlet
broker = 'redis://127.0.0.1:6379/0'
# backend
backend = 'redis://127.0.0.1:6379/1'
# worker
app = Celery('main', broker=broker, backend=backend, include=['celery_task.tasks', 'celery_task.home_task'])

# 时区
app.conf.timezone = 'Asia/Shanghai'
# 是否使用UTC
app.conf.enable_utc = False

# 任务的定时配置
from datetime import timedelta
from celery.schedules import crontab

app.conf.beat_schedule = {
    'update_banner': {
        'task': 'celery_task.home_task.update_banner',
        'schedule': timedelta(days=1),
        'args': (),
    },
    # 'send_msg': {
    #     'task': 'celery_task.tasks.send_sms',
    #     'schedule': timedelta(seconds=10),
    #     'args': ('15249783301', '8888')
    # }
}

celery_task/tasks.py

from .celery import app
import time

@app.task
def seckill(shop_id):
    time.sleep(5)  # 只是为了模拟一下延迟,假如有很大的任务量
    return shop_id

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

异步任务立即执行

任务.dealy(参数)
# 秒杀就是异步立即执行

延迟任务

-任务.apply_async(args=[参数],eta=时间对象)
-任务.apply_async(args=[参数],countdown=10)
-retry:如果任务失败后,是否重试,默认为True


from datetime import datetime, timedelta
eta = datetime.utcnow() + timedelta(seconds=5)
res = tasks.seckill.apply_async(args=(shop_id,), eta=eta)


res = tasks.seckill.apply_async(args=(shop_id,), countdown=5) 

定时任务---》写配置

-写配置celery.py中
    	from datetime import timedelta
        from celery.schedules import crontab
        app.conf.beat_schedule = {
            'send_sms': {
                'task': 'celery_task.user_task.send_sms',
                'schedule': timedelta(seconds=3),
                # 'schedule': crontab(hour=8, day_of_week=1),  # 每周一早八点
                'args': ('1895367221', '8888'),
            }
        }
        
        
        
    -启动worker
    	celery -A celery_task worker -l info -P eventlet
        
        
    # 需要再启动一个服务,定时给celery提交任务
    -启动beat:每隔3s,向队列中提交一个任务
    	celery -A celery_task beat -l info

双写一致性

# 学了redis,首页轮播图接口---》来一个访问首页,就会去mysql查询一次,假设并发量很高,一万个人访问首页,轮播图接口就会被查1w次,有1w的的mysql查询
# 在轮播图接口中,在返回给前端之前,把轮播图接口的数据,在redis中放一份
# 以后,用户访问轮播图接口,先去redis中取,如果有直接返回,如果没有,取mysql取出来,放到redis中,再返回

接口加缓存后,效率高了,但是存在问题

-mysql和redis数据不一致的问题
-出现了双写一致性问题

解决双写一致性问题

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

接口加缓存

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

    def list(self, request, *args, **kwargs):
        # 请求来了,先去redis中查询,如果有,直接返回
        banner_list = cache.get('banner_list_cache')
        if banner_list:
            logger.info('走了缓存')
            return Response(data=banner_list)
        else:
            res = super().list(request, *args, **kwargs)
            logger.info('走了mysql')
            cache.set('banner_list_cache', res.data)
            return res

cleler_task/home_task

from .celery import app
from home.models import Banner
from django.conf import settings
from home.serialzier 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)
    for item in ser.data:
        item['image'] = settings.HOST_URL + item['image']
    # 放到redis中
    cache.set('banner_list_cache', ser.data)
    return True

celery.py

# 一、加载django配置环境
import os
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "luffyapi.settings.dev")
import django

django.setup()
# 二、加载celery配置环境
from celery import Celery
eventlet
broker = 'redis://127.0.0.1:6379/0'
# backend
backend = 'redis://127.0.0.1:6379/1'
# worker
app = Celery('main', broker=broker, backend=backend, include=['celery_task.home_task'])

# 时区
app.conf.timezone = 'Asia/Shanghai'
# 是否使用UTC
app.conf.enable_utc = False

# 任务的定时配置
from datetime import timedelta
from celery.schedules import crontab

app.conf.beat_schedule = {
    'update_banner': {
        'task': 'celery_task.home_task.update_banner',
        'schedule': timedelta(days=1),
        'args': (),
    },
}

启动beat启动worker

每隔一段时间,向队列中提交一个任务
celery -A celery_task beat -l info

worker:

celery -A celery_task worker -l info -P eventlet

django中使用celery

使用步骤

第一步:celery_task 放到项目根路径下
    -celery.py
    -xx_task.py
 第二步:再celery.py 中
    import os
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "luffyapi.settings.dev")
    import django
    django.setup()
 第三步:django项目中引入任务,直接使用延迟,异步任务提交
    任务.delay()
    任务.apply_async()
 第四步:启动worker
 第五步:启动beat

注意

1 task可以写道不同的app中,注意再celery.py 中include的时候,路径要对
2 可以把celery运行在多台机器上---》完整的项目copy到机器上---》启动worker
    不启动djagno
posted @ 2022-07-17 17:05  香菜根  阅读(157)  评论(0)    收藏  举报