ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

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

2022-07-15 18:00:09  阅读:143  来源: 互联网

标签:task res celery 任务 import id 双写


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

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有