首页 > 其他分享 >【21.0】结合celery改造接口

【21.0】结合celery改造接口

时间:2023-08-19 17:22:21浏览次数:42  
标签:code 缓存 21.0 接口 celery luffyCity task import data

【一】引入

  • 所有接口都可以改造,尤其是查询所有的这种接口,如果加入缓存,会极大的提高查询速度

  • 首页轮播图接口:

    • 获取轮播图数据,加缓存---》咱们只是以它为例

【二】改造轮播图接口

  • luffyCity\luffyCity\apps\home\views.py
class BannerView(GenericViewSet, CommonListModelMixin):
    # 过滤出没有被删除 + 可以显示 + 优先级排序
    queryset = Banner.objects.all().filter(is_delete=False, is_show=True).order_by('orders')[:settings.BANNER_COUNT]
    serializer_class = BannerSerializer

    def list(self, request, *args, **kwargs):
        '''
        (1)在缓存中查询有没有数据
        (2)如果有,直接返回,不走父类的list
        (3)如果没有,走父类的list,查询数据库,返回数据
        (4)返回数据,存入缓存
        '''

        data = cache.get('banner_list')

        # 缓存中没有数据
        if not data:
            # 查询数据库
            res = super().list(request, *args, **kwargs)  # 查询数据库
            # 存入缓存
            data = res.data.get("data")
            cache.set('banner_list', data)
        # 缓存中有数据
        return CommonResponse(data=data)

【三】首页轮播图之双写一致性问题解决

  • celery定时更新轮播图资源
  • luffyCity\celery_task\home_task.py
# -*-coding: Utf-8 -*-
# @File : home_task .py
# author: Chimengmeng
# blog_url : https://www.cnblogs.com/dream-ze/
# Time:2023/8/11
from .celery import app
from django.conf import settings
from django.core.cache import cache
from luffyCity.apps.home.models import Banner
from luffyCity.apps.home.serializers.Banner_serializer import BannerSerializer

@app.task
def update_banner():
    # 查询数据库,拿到所有的轮播图资源,放到缓存中
    banner_list = Banner.objects.all().filter(is_delete=False, is_show=True).order_by('orders')[:settings.BANNER_COUNT]
    banner_ser = BannerSerializer(instance=banner_list, many=True)
    # 如果在视图类中,因为有request对象,所以会自动帮我们拼接路径
    # 在task中没有request对象,所以需要自己拼接路径
    for item in banner_ser.data:
        item['image'] = settings.BACKEND_URL + item['image']
    # 存到缓存中
    cache.set('banner_list', banner_ser.data)
    return "更新成功"
  • luffyCity\celery_task\celery.py
# -*-coding: Utf-8 -*-
# @File : celery .py
# author: Chimengmeng
# blog_url : https://www.cnblogs.com/dream-ze/
# Time:2023/8/11
import os
from datetime import timedelta
from celery import Celery
from celery.schedules import crontab
# celery 需要 注册Django
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "luffyCity.settings.dev")

# 消息中间件
broker = "redis://127.0.0.1:6379/0"
# 存储结果
backend = "redis://127.0.0.1:6379/1"
# 存放需要处理任务的列表
include = ['celery_task.user_task','celery_task.home_task']
# 实例化得到celery对象
app = Celery(__name__, backend=backend, broker=broker, include=include)

# APP 配置 +---+ 定时任务配置
app.conf.beat_schedule = {
    # 定时更新banner资源
    'update_banner': {
        # 执行的任务函数
        'task': 'celery_task.home_task.update_banner',
        # 延迟时间
        # 'schedule': crontab(hour=8, day_of_week=1),  # 每周一早八点
        # 'schedule': crontab(hour=11, minute=35),  # 每天11点35,执行
        'schedule': timedelta(minutes=5),
        'args': (),
    },
}
  • luffyCity\luffyCity\apps\home\views.py
from django.shortcuts import render, HttpResponse
from django.core.cache import cache
from rest_framework.viewsets import GenericViewSet
from django.conf import settings
from luffyCity.apps.home.models import Banner
from luffyCity.apps.home.serializers.Banner_serializer import BannerSerializer
from luffyCity.utils.common_mixin import CommonListModelMixin
from luffyCity.utils.common_response import CommonResponse


class BannerView(GenericViewSet, CommonListModelMixin):
    # 过滤出没有被删除 + 可以显示 + 优先级排序
    queryset = Banner.objects.all().filter(is_delete=False, is_show=True).order_by('orders')[:settings.BANNER_COUNT]
    serializer_class = BannerSerializer

    def list(self, request, *args, **kwargs):
        '''
        (1)在缓存中查询有没有数据
        (2)如果有,直接返回,不走父类的list
        (3)如果没有,走父类的list,查询数据库,返回数据
        (4)返回数据,存入缓存
        '''

        data = cache.get('banner_list')

        # 缓存中没有数据
        if not data:
            # 查询数据库
            res = super().list(request, *args, **kwargs)  # 查询数据库
            # 存入缓存
            data = res.data.get("data")
            cache.set('banner_list', data)
        # 缓存中有数据
        return CommonResponse(data=data)

【四】异步发送短信

  • luffyCity\celery_task\user_task.py
# -*-coding: Utf-8 -*-
# @File : user_task .py
# author: Chimengmeng
# blog_url : https://www.cnblogs.com/dream-ze/
# Time:2023/8/11

from .celery import app
import time
from luffyCity.libs.SMS_TencentCloud_Sender import tencent_sms_main


@app.task
def send_sms(mobile, code):
    res = tencent_sms_main(code, mobile)
    if res:
        return f'{mobile}的验证码:>>{code}已发送'
    else:
        return f'{mobile}的验证码:>>{code}发送失败'
  • luffyCity\luffyCity\apps\user\views.py
@action(methods=['GET'], detail=False)
def send_sms(self, request, *args, **kwargs):

    # 前端把需要发送验证码的手机号传入,携带在地址栏中
    mobile = request.query_params.get('mobile', None)
    code = get_verify_code(4)  # 存储验证码,放到缓存内
    cache.set(f'sms_code_{mobile}', code)
    if mobile:
        # # 开启线程处理短信
        # # tencent_sms_main(verify_code=code, tag_phone=mobile)
        # t = Thread(target=tencent_sms_main, args=(code, mobile,))
        # t.start()

        # 异步发送短信 ---- 向管道 提交异步任务
        res = send_sms.delay(code=code, mobile=mobile)
        # 将任务提交到数据库存储,方便后续查询


        return CommonResponse(msg="验证码已发送")
    raise APIException("请输入手机号")

【五】异步秒杀前后端

【1】思维导图

【2】前端逻辑实现

  • lufycity_web\src\views\SkillView.vue
<script setup>

</script>

<template>
  <div>
    <h2>Go语言从入门到放弃</h2>
    <el-button type="danger" round @click="handleKill">秒杀</el-button>
  </div>
</template>

<script>

export default {
  name: 'Go',
  data() {
    return {
      t: null,
      task_id: null,
    }
  },
  methods: {
    handleKill() {
      this.$axios.post(`${this.$settings.BASE_URL}user/sckill/`, {
        name: "萌萌抱枕",
      }).then(res => {
        if (res.data.code == 100) {
          alert("正在秒杀")
          this.task_id = res.data.task_id
          // 定时任务,定时向后端发送请求,获取结果
          this.t = setInterval(() => {
            this.$axios.post(`${this.$settings.BASE_URL}user/sckill/?task_id=${this.task_id}/`).then(res => {
              if (res.data.code == 100 || res.data.code == 101) {
                this.$message(res.data.msg)
                // 销毁定时器
                clearInterval(this.t)
                this.t = null
              } else {
                this.$message(res.data.msg)
              }
            })
          })
        }
      })
    }
  }
}
</script>
<style scoped>

</style>
  • lufycity_web\src\router\index.js
import Vue from 'vue'
import VueRouter from 'vue-router'
import HomeView from '../views/HomeView.vue'
import SkillView from "@/views/SkillView.vue";

Vue.use(VueRouter)

const routes = [
    {
        path: '/',
        name: 'home',
        component: HomeView
    },
    {
        path: '/sckill',
        name: 'sckill',
        component: SkillView
    },

]

const router = new VueRouter({
    mode: 'history',
    base: process.env.BASE_URL,
    routes
})

export default router

【3】后端逻辑实现

  • luffyCity\luffyCity\apps\user\views.py
class SkillView(APIView):
    def post(self, request, *args, **kwargs):
        name = request.data.get('name')

        # 提交秒杀异步任务
        res = shill_goods.delay(name)
        return CommonResponse(task_id=str(res))

    def get(self, request, *args, **kwargs):
        back_dict = {"code": 200, "result": "处理完成"}
        # 查询状态
        task_id = request.query_params.get('task_id')
        result = AsyncResult(id=task_id, app=app)
        if result.successful():  # 正常执行完成
            result = result.get()  # 任务返回的结果
            if result:

                return CommonResponse(code=100, msg='秒杀成功')
            else:
                back_dict['code'] = 101
                back_dict['result'] = '秒杀失败'
            return back_dict
        elif result.failed():
            return CommonResponse(code=102, msg='任务失败')
        elif result.status == 'PENDING':
            return CommonResponse(code=201, msg='任务等待被执行')
        elif result.status == 'RETRY':
            return CommonResponse(code=301, msg='任务异常,正在重试')
        elif result.status == 'STARTED':
            return CommonResponse(code=202, msg='任务已开始')
        else:
            return CommonResponse(code=102, msg='秒杀任务异常')
  • luffyCity\luffyCity\apps\user\urls.py
from django.urls import path
from .views import UserView, SkillView
from rest_framework.routers import SimpleRouter

router = SimpleRouter()
router.register('userinfo', UserView, 'userinfo')
urlpatterns = [
    # http://127.0.0.1:8000/api/v1/user/sckill/
    path('sckill/', SkillView.as_view())
]
urlpatterns += router.urls
  • luffyCity\celery_task\user_task.py
import random

from .celery import app
import time
from luffyCity.libs.SMS_TencentCloud_Sender import tencent_sms_main



@app.task
def shill_goods(name):
    # 开启事务 ---- 扣减库存 --- 生成订单
    # 模拟排队
    time.sleep(2)
    res = random.choice([100, 102])
    if res == 100:

        print(f'{name}的商品秒杀成功')
        return True
    else:
        print(f'{name}的商品秒杀失败')
        return False

【补充】双写一致性问题

  • 缓存数据和数据库数据不一致了
  • 双写一致性问题是指在使用缓存系统的情况下,当缓存数据和数据库数据发生不一致时,需要采取相应的策略来保证数据的一致性。

【1】写入数据库,删除缓存:

  • 这种策略是在写入数据库之后,主动删除相关缓存数据。
  • 当有需要读取该数据的请求时,缓存系统会重新从数据库中获取最新数据并进行缓存,确保缓存和数据库数据保持一致。
  • 这种策略适用于读取请求频率较高,而更新请求较低的场景。

【2】写入数据库,更新缓存:

  • 这种策略是在写入数据库之后,触发缓存更新操作,将最新的数据存入缓存中,确保缓存与数据库数据保持一致。
  • 当有读取请求时,可以直接从缓存中获取数据,提高读取性能。
  • 这种策略适用于数据的读取请求频率和更新请求频率都较高的场景。

【3】定时更新缓存:

  • 这种策略是通过定时任务或者其他方式,在一定的时间间隔内对缓存进行更新,将数据库中的数据同步到缓存中。
  • 定时更新可以根据数据的更新频率灵活设置更新时间间隔,适用于读取请求相对较低,而更新请求频率较高的场景。

【补充】缓存接口的更多方式

【1】封装成类

  • 继承某个类,自动加缓存

【2】封装成装饰器

  • 利用装饰器自动走,加缓存

标签:code,缓存,21.0,接口,celery,luffyCity,task,import,data
From: https://www.cnblogs.com/dream-ze/p/17642743.html

相关文章

  • api接口的使用原理是什么?
    随着互联网的发展和不同系统之间的交互越来越频繁,API接口的使用已经成为软件开发和集成中不可或缺的一部分。API接口的使用原理是通过预定义的接口规范,软件系统可以调用或提供API接口的服务,来实现不同系统之间的数据传输和功能调用。本文将详细介绍API接口的使用原理,包括API接口的......
  • api接口的使用原理是什么?
    ​随着互联网的发展和不同系统之间的交互越来越频繁,API接口的使用已经成为软件开发和集成中不可或缺的一部分。API接口的使用原理是通过预定义的接口规范,软件系统可以调用或提供API接口的服务,来实现不同系统之间的数据传输和功能调用。本文将详细介绍API接口的使用原理,包括API接......
  • 模拟应用网关下游系统的一些场景测试接口
    场景:构造一个返回请求参数(表单入参),请求header,设置响应header的测试demo接口框架:springboot@ResponseBody@RequestMapping("/test/api/v1")publicMapserverPostTestv1(HttpServletRequesthttpRequest,HttpServletResponsehttpResponse,@RequestHeaderMultiValueMap<Str......
  • I/O流、stream流、函数式接口
    I/O流intern()方法:首先会去常量池中查看是否存在这样的对象,如果不存在,则创建,创建好之后再将这个常量池中创建的内存地址赋值给相应的引用。Strings4=s3.intern();Strings5="管理员很水";//由于s4已经在常量池中创建了字符串"管理员很水",此时只需要将这个字符串的内存地址赋......
  • 使用接口管理微信收藏夹
    获取收藏信息小提示:获取收藏相关信息注意:该接口作用不大请求URL:http://域名地址/api/favor/getinfo请求方式:POST请求头:Content-Type:application/jsonX-GEWE-TOKEN:后台获取参数:参数名称数据类型必填说明appid是string设备idsync_key是string返回数据:参数名数据类型说明retnumber0:......
  • fastjson对接口参数的某个字段不打印输出,如文件的base64字符串
    fastjson对接口参数的某个字段不打印输出,如文件的base64字符串packagecom.example.core.mydemo.json5;importcom.alibaba.fastjson.JSON;importcom.alibaba.fastjson.annotation.JSONField;/**需要提供getset方法,如果使用@Datalombok不生效(关键)**publicclassIte......
  • C++中String的语法及常用接口用法
    在C语言中,string是一个标准库类(class),用于处理字符串,它提供了一种更高级、更便捷的字符串操作方式,string 类提供了一系列成员函数和重载运算符,以便于对字符串进行操作和处理。一、string类在学习string前,我们不妨先来了解一下string类到底是什么,有什么用呢?我们先来了解一下基本......
  • 登陆接口实现返回token
    自定义登陆接口,然后让SpringSecurity对这个接口放行,让用户访问这个接口的时候不用登录也能访问。在接口中我们通过AuthenticationManager的authenticate方法来进行用户认证,所以需要在SecurityConfig中配置把AuthenticationManager注入容器。认证成功的话要生成一个jwt,放入响......
  • 业务开发时,接口不能对外暴露怎么办?
    在业务开发的时候,经常会遇到某一个接口不能对外暴露,只能内网服务间调用的实际需求。面对这样的情况,我们该如何实现呢?今天,我们就来理一理这个问题,从几个可行的方案中,挑选一个来实现。推荐一个开源免费的SpringBoot实战项目:https://github.com/javastacks/spring-boot-best-p......
  • 文字转语音 - 搭建微软tts整合web服务提供api接口(免费)
     微软tts是业界公认文字转语音效果最佳本文使用docker搭建微软tts服务并提供api接口对外提供服务对接官方免费在线体验接口,搭建后可免费进行调用使用,不保证永久稳定可用调用方式url:http://127.0.0.1:5003/ttsmethod:POST参数 类型 描述text string 语音文字内容voiceName stri......