其他分享
首页 > 其他分享> > celery的安装和使用,celery包结构,秒杀逻辑,异步任务,延迟任务,定时任务,双写一致性,django中使用celery

celery的安装和使用,celery包结构,秒杀逻辑,异步任务,延迟任务,定时任务,双写一致性,django中使用celery

作者:互联网

celery介绍架构和安装

# celery:芹菜,分布式异步任务框架
# 注意:Celery is a project with minimal funding, so we don’t support Microsoft Windows. Please don’t open any issues related to that platform.  ---》使用别的方式可以运行在win上

# celery可以做的事:
	1 异步执行:解决耗时任务,将耗时操作任务提交给Celery去异步执行,比如发送短信/邮件、消息推送、音视频处理等等
	2 延迟执行:解决延迟任务
	3 定时执行:解决周期(周期)任务,比如每天数据统计
    
# celery的概念:celery是独立的服务,跟其它框架无关,需要单独启动
	"""
    1)可以不依赖任何服务器,通过自身命令,启动服务
    2)celery服务为为其他项目服务提供异步解决任务需求的
    注:会有两个服务同时运行,一个是项目服务,一个是celery服务,项目服务将需要异步处理的任务交给celery服务,celery就会在需要时异步完成项目的需求

    人是一个独立运行的服务 | 医院也是一个独立运行的服务
        正常情况下,人可以完成所有健康情况的动作,不需要医院的参与;但当人生病时,就会被医院接收,解决人生病问题
        人生病的处理方案交给医院来解决,所有人不生病时,医院独立运行,人生病时,医院就来解决人生病的需求
    """
	
 # celery的架构
	# 1 消息中间件broker---》redis,rabbitmq
    # 2 任务执行单元worker--》celery的进程
    # 3 结果存储(backend):Task result store---》redis,mysql,rabbitmq。。。

img

celery基本使用

# pip install celery

# 第一步:实例化得到app对象,注册任务
    from celery import Celery
    backend='redis://127.0.0.1:6379/1' # 结果存储

    broker='redis://127.0.0.1:6379/2'  # 消息中间件

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


    #任务 --->变成了celery的任务了
    @app.task
    def add(a,b):
        import time
        time.sleep(2)
        return a+b
    
# 第二步:提交任务---第三方来提交
    from main import add
    res=add.delay(4,5)  # 把任务提交到消息中间件中
    
# 第三步:使用celery 来执行任务:启动worker
    # win上:装eventlet
    celery -A s1 worker -l info -P eventlet
    # linux、mac上
     celery -A main worker -l info 
        
        
# 第四步:查看任务执行结果---》手动去redis中看

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'])

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/test/', {
        id: '999'
      }).then(res => {
        this.$message({
          message: res.data.msg,
          type: 'warning'
        });

        let taskId = res.data.task_id
        setInterval(() => {
          this.$axios.get('http://127.0.0.1:8000/test/?task_id=' + taskId).then(res => {
            this.$message({
              message: res.data.msg,
              type: 'warning'
            });
          })
        }, 3000)


      })

    }

  }


}
</script>

<style scoped>

</style>

后端

class TestView(APIView):
    # def post(self, request):
    #     id=request.data.get('id')
    #     # 同步操作:执行秒杀逻辑--》函数--》假设耗时10s---》返回给前端
    #     # res=seckill('1001',id)
    #
    #     # 异步操作--->瞬间就返回了任务id号,这个任务至于做没做,不知道
    #     res=seckill.delay('1001',id)
    #
    #     return APIResponse(msg='您正在排队',task_id=str(res))

    def post(self, request):

        # res=seckill.delay('1001',id)  # 异步任务,立即执行

        ## 延迟10s后发送短信
        # eta传时间对象,到这个时间,再执行
        # from datetime import datetime, timedelta
        # eta = datetime.utcnow() + timedelta(seconds=10)
        # res=send_sms.apply_async(args=('18933333', '9999'), eta=eta)   # 延迟任务
        res = send_sms.apply_async(args=('18933333', '9999'), countdown=5)  # 延迟任务

        return APIResponse(msg='您正在排队', task_id=str(res))

    def get(self, resquest):
        task_id = resquest.GET.get('task_id')
        from celery_task.celery import app
        from celery.result import AsyncResult
        asy = AsyncResult(id=task_id, app=app)
        if asy.successful():
            result = asy.get()
            return APIResponse(msg='秒杀成功')
        elif asy.failed():
            return APIResponse(msg='秒杀失败')
        else:
            return APIResponse(msg='请继续等待')

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

# 异步任务立即执行
	-任务.dealy(参数)

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

# 定时任务---》写配置
	-写配置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
    -启动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任务

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

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

标签:task,res,celery,任务,import,id,双写
来源: https://www.cnblogs.com/zaoan1207/p/16482335.html