一、装饰器总结 * 普通函数装饰器 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左右!
|