接口缓存

1 查询所有接口(带过滤)--》每次都要去查询,性能不高

2 一旦查出来,下次还用这个数据的话--数据存放在缓存中---》直接给

3 首页轮播图

class BannerView(GenericViewSet, APIListModelMixin):
    queryset = Banner.objects.all().filter(is_delete=False, is_show=True).order_by('orders')[
               0:settings.BANNER_COUNT]  # 过滤排序
    serializer_class = Bannerserializer

    # 接口缓存
    def list(self, request, *args, **kwargs):
        banner_list = cache.get('banner_list')
        if not banner_list:
            # 缓存中没有数据,走数据库
            res = super().list(request, *args, **kwargs)
            banner_list = res.data.get('results')  # 列表类型
            # 放到django的缓存中
            cache.set('banner_list', banner_list)
        return APIResponse(results=banner_list)

1.1带缓存的mixin类

 1 class CacheAPIListModelMixin(ListModelMixin):
 2     cache_key = None
 3 
 4     def list(self, request, *args, **kwargs):
 5         assert self.cache_key, APIException('如果继承CacheAPIListModelMixin,必须要加类属性')
 6         # 1  先去缓存中取,根据key
 7         results = cache.get(self.cache_key)
 8         if not results:
 9             logger.info('走了数据库----')
10             res = super().list(request, *args, **kwargs)
11             results = res.data
12             # 2  存入缓存中
13             cache.set(self.cache_key, results)
14         return APIResponse(results=results)

视图类 home.view

1 from utils.common_mixin import CacheAPIListModelMixin
2 
3 
4 class BannerView(GenericViewSet, CacheAPIListModelMixin):
5     queryset = Banner.objects.all().filter(is_delete=False, is_show=True).order_by('orders')[:settings.BANNER_COUNT]
6     serializer_class = Bannerserializer
7     cache_key = 'banner_list'

.

加入缓存后存在问题

# 1 数据从缓存拿--》如果mysql中数据改了--》缓存中一直有数据---》不会做自动更新---》缓存中的数据和mysql中的数据--》不一致了

# 2 缓存不一致;双写一致性

# 3 解决这个问题:
    -1 增,改,删 mysql--》删缓存
    -2 增,改,删 mysql--》更新缓存
    -3 定时更新缓存--》会有延迟

celery介绍

 1  0。 celery:分布式异步任务框架,celery 芹菜,吉祥物是芹菜
 2 
 3  1。 celery 是一个灵活且可靠的,处理大量消息的分布式系统,可以在多个节点之间处理某个任务
 4 
 5  2。 celery 是一个专注于实时处理的任务队列,支持任务调度
 6 
 7  3。 celery 是开源的,有很多的使用者
 8 
 9  4。 celery 完全基于 Python 语言编写
10 
11  5。 所以 celery 本质上是一个分布式的异步任务调度框架,类似于 Apache 的 airflow
12 
13  6。 celery 只是用来调度任务的,但它本身并不具备存储任务的功能,而调度任务的时候肯定是要把任务存起来的。
  因此要使用 celery 的话,还需要搭配一些具备存储、访问功能的工具,比如:消息队列、Redis缓存、数据库等等。官方推荐的是消息队列 RabbitMQ,我们使用 Redis
14 15 16 ### celery能做什么### 17 1 定时任务 18 2 异步任务 19 3 延迟任务

1.2celery 使用场景

1 异步任务
    -一些耗时的操作可以交给celery异步执行,而不用等着程序处理完才知道结果。
    -视频转码、邮件发送、消息推送等等
2 定时任务
    -定时推送消息、定时爬取数据、定时统计数据等

3 延迟任务
    -提交任务后,等待一段时间再执行某个任务

1.3 Celery官网

# 1 开源地址(源码)
https://github.com/celery/celery
    
# 2  官网
https://docs.celeryq.dev/en/stable/
    
# 3 最新版本
Celery (5.4)

#4 python支持
Celery version 5.3 runs on
Python ❨3.8, 3.9, 3.10, 3.11#5 Django支持
Celery 5.3.x supports Django 2.2 LTS or newer versions. Please use Celery 5.2.x for versions older than Django 2.2 or Celery 4.4.x if your Django version is older than 1.11

1.4 celery架构

# Celery 架构,它采用典型的生产者-消费者模式,主要由以下部分组成:

# 1 Celery Beat,任务调度器,Beat 进程会读取配置文件的内容,周期性地将配置中到期需要执行的任务发送给任务队列。

# 2 Producer:需要在队列中进行的任务,一般由用户、触发器或其他操作将任务入队,然后交由 workers 进行处理。调用了 Celery 提供的 API、函数或者装饰器而产生任务并交给任务队列处理的都是任务生产者。

# 3 Broker,即消息中间件,在这指任务队列本身,Celery 扮演生产者和消费者的角色,brokers 就是生产者和消费者存放/获取产品的地方(队列)。

# 4 Celery Worker,执行任务的消费者,从队列中取出任务并执行。通常会在多台服务器运行多个消费者来提高执行效率。

# 5 Result Backend:任务处理完后保存状态信息和结果,以供查询。Celery 默认已支持 Redis、RabbitMQ、MongoDB、Django ORM、SQLAlchemy 等方式。



实际应用中,用户从 Web 前端发起一个请求,我们只需要将请求所要处理的任务丢入任务队列 broker 中,由空闲的 worker 去处理任务即可,处理的结果会暂存在后台数据库 backend 中。
我们可以在一台机器或多台机器上同时起多个 worker 进程来实现分布式地并行处理任务。

 。

celery快速使用

1.1安装

# 0 创建Python项目

# 1 创建虚拟环境

# 2 安装celery pip install celery

# 3 安装redis(消息队列和结果存储使用redis) pip install redis

# 4 安装eventlet(win 平台,如果是mac,linux不需要) pip install eventlet

1.2快速使用

celery_demo.py--主文件

main.py

 1 from celery import Celery
 2 import time
 3 
 4 # 创建一个Celery实例
 5 broker = 'redis://127.0.0.1:6379/1'
 6 backend = 'redis://127.0.0.1:6379/2'
 7 app = Celery('celery_test', broker=broker, backend=backend)
 8 
 9 
10 # 定义任务
11 @app.task
12 def add(n, m):
13     time.sleep(2)
14     print('n+m的结果:%s' % (n + m))
15     return n + m
16 
17 
18 @app.task
19 def send_email(mail='1922517453@qq.com'):
20     print('模拟发送延迟--开始')
21     time.sleep(2)
22     print('模拟发送延迟--结束')
23     return '邮件发送成功:%s' % mail

add_task.py--提交异步任务

# 提交任务文件
from main import add, send_email

# 同步执行任务
# res = add(3, 4)
# print(res)

# 2. 异步执行任务,提交到broker消息队列
res = add.delay(3, 4)
print(res)  # 返回任务id号 02358487-658e-4815-8f23-2da1ac919898

1.3查看提交的任务

 

1.4让worker执行任务

1 # 通过命令启动worker
2 # win启动
3 celery -A main worker -l info -P eventlet
4 # mac linux
5 celery -A main worker -l info 

 

1.5 结果存储查看结果


# 1 直接看redis 有数据

# 2 通过代码,拿到结果

from
main import app from celery.result import AsyncResult id = '5a8d1d68-1963-4771-9889-839bd954a37b' if __name__ == '__main__': result = AsyncResult(id=id, app=app) if result.successful(): result = result.get() print(result) elif result.failed(): print('任务失败') elif result.status == 'PENDING': print('任务等待中被执行') elif result.status == 'RETRY': print('任务异常后正在重试') elif result.status == 'STARTED': print('任务已经开始被执行')

celery包结构

 

 1 celery.py
 2 
 3 from celery import Celery
 4 import time
 5 broker = 'redis://127.0.0.1:6379/1'
 6 backend = 'redis://127.0.0.1:6379/2'
 7 # 1 创建app对象
 8 app = Celery('app', broker=broker, backend=backend,include=['celery_task.order_task','celery_task.user_task'])
 9 
10 ------------------------------------------------
11 
12 user_tasks.py
13 
14 from .celery import app
15 import time
16 # 用户相关任务
17 @app.task
18 def add(x,y):
19     time.sleep(3)
20     return x+y
21 
22 --------------------------------------------------
23 order_task.py
24 
25 from .celery import app
26 # 订单相关任务
27 # 下单成功,发送短信
28 @app.task
29 def send_sms(mobile,code):
30     print(f'手机号:{mobile},下单成功')
31     return True
32 
33 -------------------------------------

4.2 提交任务add_task.py

1 from celery_task.order_task import send_sms
2 
3 # 1  同步调用
4 # res=send_sms('111111',888)
5 # print(res)
6 
7 # 2 提交到任务队列被 worker执行
8 res = send_sms.delay('18896982015', 9999)
9 print(res)  # 282fd101-1047-427e-a4d6-867c36ec70ee

查看任务结果get_result.py

 

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

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

启动worker

# 包所在目录下,启动worker

  -celery -A celery_task worker -l debug -P eventlet

 

执行异步--延迟--定时

 1 # 1 刚刚学的是:异步任务
 2     任务名.delay()
 3     
 4 # 2 延迟任务
 5     任务名.apply_async
 6     from celery_task.user_task import add
 7     from datetime import datetime, timedelta
 8     # datetime.utcnow() 取utc时间---》默认使用utc时间--》修改配置文件做中国时区
 9     eta=datetime.utcnow() + timedelta(seconds=30)
10     res=add.apply_async(args=[5,6],eta=eta)
11     
12 # 3 定时任务:配置文件
13     3.1 配置文件  在celery.py里面操作
14     # 时区
15     app.conf.timezone = 'Asia/Shanghai'
16     # 是否使用UTC
17     app.conf.enable_utc = False
18 
19     # 任务的定时配置
20     from datetime import timedelta
21     from celery.schedules import crontab
22     app.conf.beat_schedule = {
23         'add-task': {
24             'task': 'celery_task.user_task.add',
25             'schedule': timedelta(seconds=3),
26             # 'schedule': crontab(hour=8, day_of_week=1),  # 每周一早八点
27             'args': (300, 150),
28         },
29         'send-sms-task': {
30             'task': 'celery_task.order_task.send_sms',
31             # 'schedule': timedelta(seconds=30),
32             'schedule': crontab(hour=11,minute=20),  # 每天11点20执行
33             'args': ('189232222',888),
34         },
35     }
36     3.2 启动beat
37     celery -A celery_task beat -l debug
38     3.3 启动worker

django中使用celery

通用方案

 1 # 1 把之前写的celery包,放到项目根路径中
 2 
 3 # 2 在视图函数中提交任务
 4 # from libs.tx_sms import get_code,send_sms as sms
 5 from celery_task.order_task import send_sms as sms1
 6 from celery_task.user_task import add
 7 class CeleryView(APIView):
 8     def get(self, request, *args, **kwargs):
 9         ## 1异步发送短信
10         mobile=request.query_params.get('mobile')
11         code=get_code()
12         # 使用celery做异步,提交任务
13         res=sms1.delay(mobile,code)
14         return APIResponse(msg=f'短信已发送,{str(res)}')
15 
16 
17         ## 2 异步计算
18         # x=request.query_params.get('x')
19         # y=request.query_params.get('y')
20         # # res=add(x,y)# -->请求要等:3s多
21         # res=add.delay(x,y)# -->请求直接返回
22         # return APIResponse(msg=str(res))
23         
24 # 3 启动worker
25 # 4 运行django,正常使用接口即可
26 
27 ======================================================
28 # 必须加这行代码,以后才能用django的配置
29 os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'luffytest.settings.dev')

[celery官方方案(新)]

#### 1 安装模块
pip install Django==3.2.22
pip install celery
pip install redis
pip install eventlet  #在windows环境下需要安装eventlet包
### 2  在项目目录下新建celery.py

import os
import django
from celery import Celery
from django.conf import settings

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

app = Celery('celery_demo')
# 方案一:不用
# app.conf.update(
#     BROKER_URL='redis://127.0.0.1:6379/1',
#     # BACKEND配置,使用redis
#     CELERY_RESULT_BACKEND='redis://127.0.0.1:6379/2',
#     CELERY_ACCEPT_CONTENT=['json'],
#     CELERY_TASK_SERIALIZER='json',
#     # 结果序列化方案
#     CELERY_RESULT_SERIALIZER='json',
#     # 任务结果过期时间,秒
#     CELERY_TASK_RESULT_EXPIRES=60 * 60 * 24,
#     # 时区配置
#     CELERY_TIMEZONE='Asia/Shanghai',
# )

# 方案二:
app.config_from_object('django.conf:settings', namespace='CELERY')
# 自动去每个app中寻找tasks.py文件,作为celery的任务
app.autodiscover_tasks()
# app.autodiscover_tasks(lambda: settings.INSTALLED_APPS)
### 3 在apps下的init中加入

from .celery1 import app as celery_app

__all__ = ('celery_app',)

 

 1 ###4.在配置文件中配置
 2 
 3 # celery的配置
 4 # broker配置,使用redis作为消息中间件
 5 CELERY_BROKER_URL = 'redis://127.0.0.1:6379/1'
 6 # 结果存储配置,使用redis
 7 CELERY_RESULT_BACKEND = 'redis://127.0.0.1:6379/2'
 8 CELERY_ACCEPT_CONTENT = ['json']
 9 CELERY_TASK_SERIALIZER = 'json'
10 # 结果序列化方案
11 CELERY_RESULT_SERIALIZER = 'json'
12 # 任务结果过期时间,秒
13 CELERY_TASK_RESULT_EXPIRES = 60 * 60 * 24,
14 # 时区配置
15 CELERY_TIMEZONE = 'Asia/Shanghai'
16 
17 CELERY_BEAT_SCHEDULE = {
18     'update_banner': {
19         'task': 'home.tasks.add',
20         'schedule': timedelta(seconds=5),
21         # 'schedule': timedelta(minutes=3),
22         # 这点要传值
23         'args': (2, 4),
24     },
25 }

 1 在user.view中测试
 2 
 3 from .tasks import send_email
 4 
 5 class CeleryView(APIView):
 6     def get(self, request, *args, **kwargs):
 7 
 8     # 官方方案集成celery
 9         res = send_email.delay()
10         return APIResponse(msg=f'邮件已经发送了:{str(res)}')

 

定时更新轮播图缓存

可能会存在的问题

# 1 轮播图加入了缓存---》

  缓存和数据库数据可能不一致

   -缓存双写一致性

# 2 解决不一致问题

   -1 增删改属性---》删缓存

   -2 增删改属性---》修改缓存

   -3 定时更新--》会有延迟

# 3 咱们使用 定时更新,处理缓存双写一致性问题

解决办法

写一个celery任务-更新轮播图

home_task
1
from home.models import Banner 2 from .celery import app 3 from django.core.cache import cache 4 from django.conf import settings 5 from home.serializer import Bannerserializer 6 7 8 @app.task 9 def update_banner(): 10 # 更新缓存数据 11 # 1.查询所有轮播图 12 queryset = Banner.objects.all().filter(is_delete=False, is_show=True).order_by('orders')[:settings.BANNER_COUNT] 13 serializer = Bannerserializer(queryset, many=True) 14 for item in serializer.data: 15 item['image'] = settings.BACKEND_URL + item['image'] 16 cache.set('banner_list', serializer) 17 return '轮播图缓存更新成功'

 

定时任务

 1 import os
 2 
 3 import django
 4 from celery import Celery
 5 
 6 # 必须加这行代码,以后才能用django的配置
 7 os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'luffytest.settings.dev')
 8 django.setup()
 9 broker = 'redis://127.0.0.1:6379/1'
10 backend = 'redis://127.0.0.1:6379/2'
11 # 1 创建app对象,include指定任务是哪些
12 app = Celery('app', broker=broker, backend=backend,
13              include=['celery_task.order_task', 'celery_task.user_task', 'celery_task.home_task'])
14 
15 # 定时任务
16 # print(app.conf)
17 
18 # 时区
19 app.conf.timezone = 'Asia/Shanghai'
20 # 是否使用UTC
21 app.conf.enable_utc = False
22 
23 # 任务的定时配置
24 from datetime import timedelta
25 from celery.schedules import crontab
26 
27 app.conf.beat_schedule = {
28     # 轮播图定时任务
29     'update-banner-task': {
30         'task': 'celery_task.home_task.update_banner',
31         'schedule': timedelta(minutes=1),
32         'args': (),
33     }
34     
35 }

补充:app.task和 shared_task区别

1 两个装饰器的区别

# @app.task(bind=True)装饰器:

这是Celery库中的装饰器,用于将函数注册为Celery任务。
bind=True指定任务函数的第一个参数为任务实例本身(通常命名为self),允许您在任务函数内部访问任务实例的属性和方法。

# @shared_task(bind=True)装饰器:

这是Celery的另一个装饰器,用于将函数注册为共享任务(shared task)。
共享任务是一种特殊类型的任务,可以跨多个Celery应用程序共享和调用。
如果您的任务需要在多个Celery应用程序中共享,或者您希望使用共享任务的特性,那么可以考虑使用此装饰器。
 

#  使用建议

哪个装饰器更好取决于您的具体需求和使用情况:

如果您只是在单个Celery应用程序中定义和使用任务函数,那么@app.task(bind=True)装饰器可能更适合。

如果您需要共享任务或跨多个Celery应用程序使用任务,那么@shared_task(bind=True)装饰器可能更适合。

补充2:定时任务配置(app中任务文件名必须叫tasks)

admin配置定时任务

# 1 之前,定时任务都要在配置文件中配置,项目启动前,就定死了,后期不能改,也不能停

# 2 通过settings.py的配置可以实现定时任务的配置,做为实际项目中可能还是不够实用,更加工程化的做法是将定时任务的配置放到数据库里通过界面来配置。

# 3 Celery对此也提供了很好的支持,这需要安装django-celery-beat插件

#### 使用步骤
1 pip install django-celery-beat
2 app注册
    INSTALLED_APPS = [
    ....
    'django_celery_beat',
    ]
3 配置文件:屏蔽到原来的调度器
CELERY_BEAT_SCHEDULER = 'django_celery_beat.schedulers.DatabaseScheduler'
4 设置时区
LANGUAGE_CODE = 'zh-hans'
TIME_ZONE = 'Asia/Shanghai'
USE_I18N = True
USE_TZ = False


5 数据库迁移
python manage.py migrate django_celery_beat


6 启动woker和beat

#在两个控制台分别启动woker和beat
celery -A luffy_api.celery1 worker -l debug -P eventlet
celery -A luffy_api.celery1 beat -l debug

7 创建超级用户-访问admin的web管理端
http://127.0.0.1:8000/admin/login/

# 存在的问题
    - 周期性 :不涉及到时区,正常用
    - 固定时间的:跟时区紧密相关
        -可能会有时区问题
        
        
# 通过接口,存某些表实现

 

 

 。

admin监视任务

在控制台监控任务执行情况,还不是很方便,最好是能够通过web界面看到任务的执行情况,如有多少任务在执行,有多少任务执行失败了等 这个Celery也是可以做到了,就是将任务执行结果写到数据库中,通过web界面显示出来。 这里要用到django-celery-results插件。 通过插件可以使用Django的orm作为结果存储,这样的好处在于我们可以直接通过django的数据查看到任务状态,同时为可以制定更多的操作

#### 步骤### 
## 1 安装django-celery-results

pip install django-celery-results


##2 配置settings.py,注册app

INSTALLED_APPS = (
...,
'django_celery_results',
)


## 3 修改backend配置,将Redis改为django-db

# BACKEND配置,使用redis
#CELERY_RESULT_BACKEND = 'redis://127.0.0.1:6379/1'

# 使用使用django orm 作为结果存储
CELERY_RESULT_BACKEND = 'django-db'  #使用django orm 作为结果存储


## 4 迁移数据库

python manage.py migrate django_celery_results
# 可以看到创建了django_celery_results相关的表

## 5 admin 查看

# 6 后期,后台管理自己写,
    -可以直接使用django的orm取数据
    -也可以放到redis中,自己写接口处理

 。

Flower 监控celery任务

如果不想通django的管理界面监控任务的执行,还可以通过Flower插件来进行任务的监控。Flower的界面更加丰富,可以监控的信息更全

Flower 是一个用于监控和管理 Celery 集群的开源 Web 应用程序。它提供有关 Celery workers 和tasks状态的实时信息

# Flower可以:
1 实时监控celery的Events
    -查看任务进度和历史记录
    -查看任务详细信息(参数、开始时间、运行时间等)

2 远程操作
    -查看workers 状态和统计数据
    -关闭并重新启动workers 实例
    -控制工作池大小和自动缩放设置
    -查看和修改工作实例消耗的队列
    -查看当前正在运行的任务
    -查看计划任务(预计到达时间/倒计时)
    -查看保留和撤销的任务
    -应用时间和速率限制
    -撤销或终止任务

3 Broker 监控
    -查看所有 Celery 队列的统计信息

    
#### 使用步骤######

## 1 安装和启动

pip install flower

#2 启动
# 方式一:
celery -A luffytest flower --port-5555
#方式二
celery --broker=redis://127.0.0.1:6379/1 flower

        
#3 浏览器访问:
http://127.0.0.1:5555/

 必须启动worker和beat,

 。

任务异常自动告警

虽然可以通过界面来监控了,但是我们想要得更多,人不可能天天盯着界面看吧,如果能实现任务执行失败就自动发邮件告警就好了。这个Celery当然也是没有问题的。 通过钩子程序在异常的时候触发邮件通知

 tasks.py中加入

 1 from celery import shared_task
 2 import time
 3 from celery import Task
 4 from django.core.mail import send_mail
 5 from django.conf import settings
 6 # 成功失败邮件告警
 7 class SendEmailTask(Task):
 8     def on_success(self, retval, task_id, args, kwargs):
 9         info = f'任务成功-- 任务id是:{task_id} , 参数是:{args} , 执行成功 !'
10         send_mail('celery任务监控成功告警', info, settings.EMAIL_HOST_USER, ["616564099@qq.com",])
11 
12         print('------------成功')
13 
14     def on_failure(self, exc, task_id, args, kwargs, einfo):
15         info = f'任务失败-- 任务id为:{task_id} , 参数为:{args} , 失败 ! 失败信息为: {exc}'
16         send_mail('celery任务监控失败告警', info, settings.EMAIL_HOST_USER, ["616564099@qq.com",])
17         print('------------失败')
18 
19     def on_retry(self, exc, task_id, args, kwargs, einfo):
20         print(f'任务id位::{task_id} , 参数为:{args} , 重试了 !  错误信息为: {exc}')
21 
22 
23 @shared_task(base=SendEmailTask, bind=True)
24 def add(a,b):
25     time.sleep(1)
26     return a+b
27 
28 
29 @shared_task()
30 def send_email(mail):
31     print(f'给{mail}发送邮件了')
32     return '成功'

django发送邮件

 1 user.view
 2 
 3 # django发送邮件,celery使用
 4 from django.core.mail import send_mail as django_send_email
 5 from django.conf import settings
 6 
 7 
 8 class EmailView(APIView):
 9     def get(self, request, *args, **kwargs):
10         to_user = request.query_params.get('to_user')
11         django_send_email('邮件标题,非常重要', '内容:你好', settings.EMAIL_HOST_USER, [to_user, ])
12         return APIResponse(msg=f'邮件发送:{str(to_user)}')
13 
14 ************************
15 dev配置
16 # 发送邮件
17 EMAIL_HOST = 'smtp.qq.com'  # 如果是 163 改成 smtp.163.com
18 EMAIL_PORT = 465
19 EMAIL_HOST_USER = 'xxx@qq.com'  # 帐号
20 EMAIL_HOST_PASSWORD = '邮箱授权码'  # 密码
21 DEFAULT_FROM_EMAIL = EMAIL_HOST_USER
22 # 这样收到的邮件,收件人处就会这样显示
23 # DEFAULT_FROM_EMAIL = 'lqz<'306334678@qq.com>'
24 EMAIL_USE_SSL = True  # 使用ssl
25 # EMAIL_USE_TLS = False # 使用tls
26 # EMAIL_USE_SSL 和 EMAIL_USE_TLS 是互斥的,即只能有一个为 True

 。

异步秒杀方案

# 秒杀功能

   - qps要高:承载住很多用户1s内把功能完成

   -创建订单

   -扣减库存

   - 效率要高

# 同步秒杀

   -假设秒杀需要10s钟,项目并发量是3,总共5个商品要秒杀

   -10s内,只有3个人能进入到系统,并且开始秒杀

# 异步秒杀

   -假设秒杀需要10s钟,项目并发量是3,总共5个商品要秒杀 -使用异步,用户提交后,立马返回

   -10s内,可以响应很多很多用户提交秒杀任务:假设提交了100个用户 -这100个用户中只有5个成功

前端

  1 <template>
  2   <div>
  3     <Header></Header>
  4     <div style="padding: 50px;margin-left: 100px">
  5       <h1>Go语言课程</h1>
  6       <img src="http://photo.liuqingzheng.top/2023%2002%2022%2021%2057%2011%20/image-20230222215707795.png"
  7            height="300px"
  8            width="300px">
  9       <br>
 10       <el-button type="danger" @click="handleSeckill" v-loading.fullscreen.lock="fullscreenLoading">秒杀课程</el-button>
 11     </div>
 12 
 13 
 14     <br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br>
 15 
 16 
 17     <Footer></Footer>
 18 
 19   </div>
 20 </template>
 21 
 22 
 23 <script>
 24 import Header from '@/components/Header'
 25 import Footer from '@/components/Footer'
 26 
 27 export default {
 28   name: "SckillView",
 29   data() {
 30     return {
 31       fullscreenLoading: false,
 32       task_id: '',
 33       t: null,
 34     }
 35   },
 36   methods: {
 37     // ##############同步秒杀##############
 38     // handleSeckill() {
 39     //   this.fullscreenLoading = true;
 40     //   this.$axios({
 41     //     url: 'http://127.0.0.1:8000/api/v1/user/seckill/seckill/',
 42     //     method: 'POST',
 43     //     data: {
 44     //       course_id: '99'
 45     //     }
 46     //   }).then(res => {
 47     //     this.fullscreenLoading = false;
 48     //     this.$message.success(res.data.msg)
 49     //   }).catch(res => {
 50     //     this.fullscreenLoading = false;
 51     //     this.$message.error(res)
 52     //   })
 53     // }
 54     // ##############同步秒杀##############
 55 
 56 
 57     // ##############异步秒杀##############
 58     handleSeckill() {
 59       this.fullscreenLoading = true;
 60       this.$axios({
 61         url: 'http://127.0.0.1:8000/api/v1/user/seckill/seckill/',
 62         method: 'POST',
 63         data: {
 64           course_id: '99'
 65         }
 66       }).then(res => {
 67         // 在排队,转圈的,还需要继续显示
 68         this.$message.success(res.data.msg)
 69         this.task_id = res.data.task_id
 70         // 继续发送请求---》查询是否秒杀成功:1 成功 2 没成功  3 秒杀任务还没执行
 71         // 启动定时任务,没隔1s,向后端发送一次请求
 72         this.t = setInterval(() => {
 73           this.$axios({
 74             url: 'http://127.0.0.1:8000/api/v1/user/seckill/get_result/',
 75             method: 'get',
 76             params: {
 77               task_id: this.task_id
 78             }
 79           }).then(res => {
 80             // 100 成功,success : 1 成功  0 失败  2 还没开始
 81             if (res.data.success == '1') {
 82               // 转圈框不显示
 83               this.fullscreenLoading = false;
 84               // 停止定时任务
 85               clearInterval(this.t)
 86               this.t = null
 87               this.$message.success(res.data.msg)
 88 
 89             } else if (res.data.success == '0') {
 90               // 转圈框不显示
 91               this.fullscreenLoading = false;
 92               // 停止定时任务
 93               clearInterval(this.t)
 94               this.t = null
 95               this.$message.error(res.data.msg)
 96             } else {
 97               // this.$message.error(res.msg)
 98               console.log(res.msg)
 99             }
100           })
101         }, 1000)
102 
103       }).catch(res => {
104         this.fullscreenLoading = false;
105         this.$message.error(res)
106       })
107     }
108   },
109   components: {
110     Header, Footer
111   }
112 }
113 </script>
114 
115 
116 <style scoped>
117 
118 </style>

后端

注意:所有关于之前写的celery都要注释掉

views.py

 1 # 秒杀
 2 from celery_task.celery import app
 3 from celery_task.user_task import seckill
 4 from celery.result import AsyncResult
 5 
 6 import random
 7 
 8 
 9 class SeckillView(ViewSet):
10     #### 同步操作,性能不高
11     # @action(methods=['POST'], detail=False)
12     # def seckill(self, request, *args, **kwargs):
13     '''
14     #1  取出传入的 课程id
15     #2 查询课程 是否还有剩余          1s
16         #2.1 有剩余,开始下单扣减库存  1s
17         #2.2,在订单表中生成一条记录   2s
18         #2.3  秒杀成功返回给前端
19     #3 课程没有剩余,秒杀失败,返回给前端
20     '''
21 
22     # course_id = request.data.get('course_id')
23     # print('根据课程id:%s,查询课程是否还有剩余,耗时3s' % course_id)
24     # time.sleep(1)
25     # res = random.choice([True, False])
26     # if res:  # 库存够
27     #     print('扣减库存,耗时3s')
28     #     time.sleep(1)
29     #     print('下单,耗时4s')
30     #     time.sleep(2)
31     #     return APIResponse(msg='恭喜您秒到了')
32     # else:
33     #     return APIResponse(code=101, msg='库存不足,秒杀失败')
34 
35     #  异步提交任务
36     @action(methods=['POST'], detail=False)
37     def seckill(self, request, *args, **kwargs):
38         course_id = request.data.get('course_id')
39         task_id = seckill.delay(course_id)
40         return APIResponse(msg='您正在排队', task_id=str(task_id))
41 
42     @action(methods=['GET'], detail=False)
43     def get_result(self, request, *args, **kwargs):
44         task_id = request.query_params.get('task_id')
45         a = AsyncResult(id=task_id, app=app)
46         if a.successful():
47             result = a.get()  # True 和 False
48             if result:
49                 return APIResponse(success='1', msg='秒杀成功')
50             else:
51                 return APIResponse(success='0', msg='秒杀失败')
52         elif a.status == 'PENDING':
53             print('任务等待中被执行')
54             return APIResponse(success='2', msg='任务等待中被执行')
55         else:
56             return APIResponse(success='3', msg='秒杀任务正在执行')

任务

 1 @app.task
 2 def seckill(course_id):
 3     print('根据课程id:%s,查询课程是否还有剩余,耗时2s' % course_id)
 4     time.sleep(2)
 5     res = random.choice([True, False])
 6     if res:  # 库存够
 7         print('扣减库存,耗时1s')
 8         time.sleep(1)
 9         print('下单,耗时2s')
10         time.sleep(2)
11         return True
12     else:
13         return False

 

posted on 2024-05-16 22:30  认真的六六  阅读(5)  评论(0编辑  收藏  举报