分享

django应用-装饰器、缓存、异步

 明德是小名儿 2023-04-12 发布于北京

一、装饰器总结

* 普通函数装饰器

  def check(func):

      def wrapper(*args, **kwargs):

          装饰逻辑

          return func(*args, **kwargs)

      return wrapper

* 视图函数装饰器

  def check(func):

      def wrapper(request, *args, **kwargs):

          装饰逻辑

          return func(request, *args, **kwargs)

      return wrapper

* 类视图函数装饰器

  def check(func):

      def wrapper(self, request, *args, **kwargs):

          装饰逻辑

          return func(self, request, *args, **kwargs)

      return wrapper

* 可传参的装饰器

  def cache_check(**cache_kwargs)

      def check(func):

          def wrapper(self, request, *args, **kwargs):

              装饰逻辑

              return func(self, request, *args, **kwargs)

          return wrapper

      return check

二、缓存与django-redis

邮件激活会议(了解)

1)背景说明

* 时间:2023/1/6 16:35:00

* 地点:快乐编程大厦1016室

* 参会人员:产品经理、后端工程师、前端工程师、测试工程师

* 会议主题:注册功能增加邮件激活,激活有效期3天

2)流程思考

* 发邮件(后端):`from django.core import mail`

* 激活链接(前端):`http://127.0.0.1:7000/dadashop/templates/active.html`

* 激活页面(前端):`做出漂亮的有灵气的温柔的可爱的激活页面`

* 激活用户(后端):`ORM更新:is_active=True`

3)用户标识

http://127.0.0.1:7000/dadashop/templates/active.html?code=zhaoliying

* 方案一:**?code=zhaoliying**   明文,不太安全!

* 方案二:**?code=base64.b64encode(b"zhaoliying")**   相对安全,但不绝对安全!

* 方案三:**?code=base64.b64encode(b"1016_zhaoliying")**  很安全!

4)随机数存储在哪里

随机数存储在Redis数据库,因为Redis基于内存速度快,并且Redis中有过期键功能,过期自动销毁。

1)DJANGO自带缓存装饰器

django自带的缓存装饰器cache_page

1. settings.py中添加缓存配置项CACHES

CACHES = {
    "default": {
        "BACKEND": "django_redis.cache.RedisCache",
        "LOCATION": "redis://127.0.0.1:6379/1",
        "OPTIONS": {
            "CLIENT_CLASS": "django_redis.client.DefaultClient",
        }
    },
    "sms": {
        "BACKEND": "django_redis.cache.RedisCache",
        "LOCATION": "redis://127.0.0.1:6379/2",
        # 默认时间300秒,设置none代表永久存储
        # "TIMEOUT": None,
        "OPTIONS": {
            "CLIENT_CLASS": "django_redis.client.DefaultClient",
        }
    },
}

2. 局部缓存

#方法一:

from django.core.cache import caches

from django_redis import get_redis_connection

#调用缓存API

# 1.存入缓存

caches["default"].set(key, value, expire)

# 2.获取数据

caches["index"].get(key)

# 3.删除指定key

caches["detail"].delete(key)

# 4.清除当前库:FLUSHDB

caches["sms"].clear()

#使用redis需先安装django-redis组件

# `sudo pip3 install django-redis`

#调用django-redis 操作更复杂的数据类型

#列表、哈希、集合、有序结合

r = get_redis_connection()

# 1.在集合中添加成员

r.sadd(key, member)

# 2.查看集合中的所有成员

r.smemebers(key)

3. 缓存整体数据

   方法一:在路由中使用

path("index",cache_page(30)(views.index_view)),  #存默认缓存区default

path("index",cache_page(60,cache="index")(views.GoodsView.as_view()))

   方法二:在视图中使用

@cache_page(60, cache="index")

def xxx_view(request):

    pass

缓存清除:因为cache_page自己构建缓存key,并且key没有任何规律,所以很难做到精准的缓存清除DEL key,所以可以使用FLUSHDB清除当前库。

2)自定义开发缓存装饰器

自己开发了一个可传参的装饰器,能够实现常规的缓存逻辑,并能实现自定义缓存的key。

from django.core.cache import caches

"""缓存装饰器(可传参的装饰器)"""

def cache_check(**cache_kwargs):

    def _cache_check(func):

        def wrapper(request, *args, **kwargs):

            """

            cache_kwargs: 装饰器中参数

            func: 被装饰器的方法

            request args kwargs: 方法参数

            """

            # cache_kwargs:{'expire': 30, 'cache': 'detail'}

            # kwargs:{'sku_id': 1}

            # 1.确认缓存中是否存在数据

            # 如果未指定缓存区域cache,则使用default

            redis =caches[cache_kwargs.get("cache", "default")]

            key = f"gd{kwargs.get('sku_id')}"

            resp = redis.get(key)

            # 2.缓存中存在:直接返回结束!

            if resp:

                return resp

            # 3.缓存中不存在:

            #   3.1 走视图函数查询

            #   3.2 存入缓存中,并返回结束!

            mysql_resp = func(request, *args, **kwargs)

            # 2.如果未指定缓存时间expire,则默认60秒

            expire = cache_kwargs.get("expire", 60)

            redis.set(key, mysql_resp, expire)

            return mysql_resp

        return wrapper

    return _cache_check

Views视图使用:

@cache_check(exprire=30,cache="detail")

def detail_view(request,sku_id):

    pass

admin.py 更新数据时,清除缓存

from django.contrib import admin

from django.core.cache import caches

from goods.models import SKU

class SKUAdmin(admin.ModelAdmin):

    list_display = ["id", "name", "price", "stock", "sales", "is_launched"]

    list_editable = ["price", "is_launched"]

    def save_model(self, request, obj, form, change):

        # 1.更新MySQL数据:执行父类逻辑

        super().save_model(request, obj, form, change)

        # 2.清除Redis缓存-详情页

        key = f"gd{obj.id}"

        caches["detail"].delete(key)

        print("更新数据时,详情页缓存清除")

        # 3.清除Redis缓存-首页

        caches["index"].clear()

        print("更新数据时,首页缓存清除")

    def delete_model(self, request, obj):

        super().delete_model(request, obj)

        key = f"gd{obj.id}"

        caches["detail"].delete(key)

        print("删除数据时,详情页缓存清除!")

        caches["index"].clear()

        print("删除数据时,首页缓存清除!")

admin.site.register(SKU, SKUAdmin)

三、异步CELERY

1)定义 简单、灵活、可靠的处理大量消息的分布式系统将大量阻塞型的任务以异步非阻塞的方式去执行。

2)核心组件

* producter生产者Django项目,用来生产任务。

* broker:消息中间件生产者只要有任务,会将任务推送至broker中存储异步(Redis | RQ)。

* worker:任务执行单元-消费者,执行异步任务(进程|协程),从消息中间件broker中获取并执行具体任务。

* backend:存储异步任务执行结果一般不使用!如果确实需要跟进异步任务的执行结果时,则使用此配置。

  

* 进程、线程、协程的区别

* 进程进程是应用程序的启动实例,拥有代码和打开的文件资源、数据资源、独立的内存空间。

* 线程线程从属于进程,是程序的实际执行者,一个进程至少包含一个主线程,也可以有更多的子线程,线程拥有自己的栈空间。 对操作系统而言,线程是最小的执行单元,进程是最小的资源管理单元。无论是进程还是线程,都是由操作系统所管理的。

* 协程是一种比线程更加轻量级的存在,正如一个进程可以拥有多个线程一样,一个线程可以拥有多个协程。协程不是被操作系统内核所管理的,仅仅是一个特殊的函数。一个线程内的多个协程虽然可以切换,但是多个协程是串行执行的,只能在一个线程内运行,没法利用CPU多核能力。这样带来的好处是性能大幅度的提升,因为不会像线程切换那样消耗资源。 

3)celery使用流程

* 创建celery的配置文件

  1.路径:和settings.py同路径。

  2.文件名:必须为celery.py。

  * 配置环境变量、初始化celery的应用、设置自动发现任务。

import os

from celery import Celery

from django.conf import settings

#1、设置环境变量

os.environ.setdefault("DJANGO_SETTINGS_MODULE","dashopt.settings")

#2、初始化celery应用

app = Celery("名字", broker="redis://127.0.0.1:6379/5")

#3、设置自动发现任务

app.autodiscover_tasks(settings.INSTALLED_APPS)

* 在各个应用下独立异步任务的tasks.py文件

from dashopt.celery import app

@app.task

def async_send_email(param1, parma2):

    pass

* 视图函数中将任务推送至消息中间件broker中(delay()

from .tasks import async_send_email

async_send_email.delay(param1, param2)

* 终端启动消费者celery worker

celery -A dashpt worker -P gevent -c 1000 -l info  #-gevent协程模式

celery -A dashopt worker -l info

* 视图测试

4)celery并发模型

* 多进程(默认prefork)`celery -A 项目名 worker -l info`

* 协程(gevent模式)  `celery -A 项目名 -P gevent -c 1000 -l info`

1000为协程数量,生产环境中一般为1000-2000左右!

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多