celery

celery介绍

复制代码
# celery:翻译过来叫芹菜,它是一个  分布式的异步任务   框架
# celery有什么用?
    1 完成异步任务:可以提高项目的并发量,之前开启线程做,现在使用celery做
    2 完成延迟任务
    3 完成定时任务
    
# 架构
    -消息中间件:broker 提交的任务(函数)都放在这里,celery本身不提供消息中间件,需要借助于第三方:redis,rabbitmq
    -任务执行单元:worker,真正执行任务的地方,一个个进程,执行函数
    -结果存储:backend,函数return的结果存储在这里,celery本身不提供结果存储,借助于第三方:redis,数据库,rabbitmq
    
复制代码

 

 

 2 celery快速使用

复制代码
# 1 celery官网:http://www.celeryproject.org/
# 2 介绍:Celery is a project with minimal funding, so we don’t support Microsoft Windows. Please don’t open any issues related to that platform

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

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

# 2 使用步骤
    -写一个main.py :实例化得到app对象,写函数,任务,注册成celery的任务
-再别的程序中:提交任务--->提交到broker中去了 add.delay(3,4) -启动worker,从broker中取任务执行,执行完放到backend中 win: celery worker -A main -l info -P eventlet # 4.x及之前用这个 celery -A main worker -l info -P eventlet # 5.x及之后用这个 lin,mac: celery worker -A main -l info celery -A main worker -l info -再backend中查看任务执行的结果 -直接看 -通过代码查看 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() #7 print(result) elif res.failed(): print('任务失败') elif res.status == 'PENDING': print('任务等待中被执行') elif res.status == 'RETRY': print('任务异常后正在重试') elif res.status == 'STARTED': print('任务已经开始被执行')
复制代码
复制代码
main.py

from celery import Celery
import time

backend = 'redis://127.0.0.1:6379/1'
broker = 'redis://127.0.0.1:6379/0'
app = Celery('test', backend=backend, broker=broker)


# 写任务,任务就是函数,加个装饰器,变成celery的任务
@app.task
def add(a, b):
    time.sleep(2)  # 假设任务耗时比较久
    print(a + b)
    return a + b
复制代码

 

celery包结构

复制代码
# 写一个celery的包,以后,再任意项目中,想用,把包copy进去,导入使用即可
    项目:
        celery_task
            -__init__.py
            -celery.py
            -user_task.py
            -home_task.py
        add_task.py
        get_result.py
    
    
    
# 使用步骤
    -新建包:celery_task
    -在包先新建一个 celery.py
    -在里面写app的初始化
    -在包里新建user_task.py 编写用户相关任务 
    -在包里新建home_task.py 编写首页相关任务 
    -其它程序,提交任务
    -启动worker ---》它可以先启动,在提交任务之前-->包所在的目录下
        celery -A celery_task worker -l info -P eventlet
    -查看任务执行的结果了
复制代码

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

复制代码
# 异步任务
    任务.delay(参数,参数)
# 延迟
    任务.apply_async(args=[参数,参数],eta=时间对象(utc时间))
# 定时
    # 定时任务配置
        -1 app的配置文件中配置 
            app.conf.beat_schedule = {
                'send_sms_task': {
                    'task': 'celery_task.user_task.send_sms',
                    'schedule': timedelta(seconds=5),
                    # 'schedule': crontab(hour=8, day_of_week=1),  # 每周一早八点
                    'args': ('1897334444', '7777'),
                },
                '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 把写好的包复制到项目路径下
    2 在包内的celery.py 的上面加入代码
        import os
        os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'luffy_api.settings.dev')
        import django
        django.setup()
    3 在django的视图类中,导入,提交任务
    4 启动worker,beat
复制代码

 

秒杀逻辑

复制代码
# 前端:
    1 秒杀按钮
    2 事件:向后端秒杀接口发送请求,发送完立马起了一个定时任务,每个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()  # 7
                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': '还在排队'})
复制代码

 

 

 7 双写一致性

7.1 接口加缓存

# 首页轮播图接口,加缓存
# 提交了接口的响应速度
# 提高并发量
复制代码
class BannerListView(ListModelMixin):
    def list(self, request, *args, **kwargs):
        if cache.get('Banner'):
            banner = cache.get('Banner')
        else:
            res = super().list(request, *args, **kwargs)
            cache.set('Banner', res.data)
            banner = res.data
        return APIResponse(data=banner)
复制代码
# 加了缓存,如果mysql数据变了,由于请求的都是缓存的数据,导致mysql和redis的数据不一致
# 双写一致性问题
    -1 修改mysql数据库,删除缓存  【缓存的修改是在后】
    -2 修改数据库,修改缓存    【缓存的修改是在后】
    -3 定时更新缓存   ---》针对于实时性不是很高的接口适合定时更新
   

# 给首页轮播图接口加入了缓存,出现了双写一致性问题,使用定时更新来解决双写一致性的问题【会存在不一致的情况,我们可以忽略】---》定时任务,celery的定时任务

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

复制代码
@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
复制代码
app.conf.beat_schedule = {
    'update_banner': {
        'task': 'celery_task.home_task.update_banner',
        'schedule': timedelta(seconds=50),
        'args': (),
    }
}
# 启动django
# 启动worker
# 启动beat

# 第一次访问:查的数据库,放入了缓存
# 以后再访问,走缓存
# 一旦mysql数据改了,缓存可能不一致
# 当时我们定时更新,最终保持了一致

 

posted @   shangxin_bai  阅读(83)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示