专题:Vue+Django REST framework前后端分离生鲜电商
Vue+Django REST framework 打造前后端分离的生鲜电商项目(慕课网视频)。
Github地址:https://github.com/xyliurui/DjangoOnlineFreshSupermarket ;
Django版本:2.2、djangorestframework:3.9.2。
前端Vue模板可以直接联系我拿。
更多内容请点击 我的博客 查看,欢迎来访。
DRF缓存功能CacheResponseMixin
加速网站访问速度,将常用的数据放在缓存中,访问这些数据优先从缓存中取。
可以访问 https://docs.djangoproject.com/zh-hans/2.2/topics/cache/ 查看Django的缓存使用方法。
但是这儿需要用的是DRF的缓存,Django的缓存不能使用。可以搜索drf-extensions或者访问 https://github.com/chibisov/drf-extensions 查看Django Rest Framework的扩展,功能很多,不只缓存功能。
官方文档 http://chibisov.github.io/drf-extensions/docs/ (为了能看这个文档,我爬到腾讯云上才访问到了)
安装drf-extensions
pip install drf-extensions
# 安装好后drf版本也被升级了
Successfully installed djangorestframework-3.10.2 drf-extensions-0.5.0
从 http://chibisov.github.io/drf-extensions/docs/#cacheresponsemixin 可以看到基本的使用方法
缓存标准viewset的
retrieve
和list
方法很常见。 这就是CacheResponseMixin
存在的原因。 只需将其混合到viewset实现中,这些方法将使用REST_FRAMEWORK_EXTENSIONS
设置中定义的函数:
- “DEFAULT_OBJECT_CACHE_KEY_FUNC” 用于
retrieve
方法 - “DEFAULT_LIST_CACHE_KEY_FUNC” 用于
list
方法
默认情况下,这些函数使用DefaultKeyConstructor
并对其进行扩展:
- 使用
RetrieveSqlQueryKeyBit
获取 “DEFAULT_OBJECT_CACHE_KEY_FUNC” - 使用
ListSqlQueryKeyBit
和PaginationKeyBit
为 “DEFAULT_LIST_CACHE_KEY_FUNC”
Mixin使用方法
from myapps.serializers import UserSerializer
from rest_framework_extensions.cache.mixins import CacheResponseMixin
class UserViewSet(CacheResponseMixin, viewsets.ModelViewSet):
serializer_class = UserSerializer
商品列表增加缓存
使用到我们的类中,修改 apps/goods/views.py 中的GoodsListViewSet(mixins.ListModelMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet)
类,CacheResponseMixin
放在继承类的第一个。
from rest_framework_extensions.cache.mixins import CacheResponseMixin
class GoodsListViewSet(CacheResponseMixin, mixins.ListModelMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet):
"""
list:
显示商品列表,分页、过滤、搜索、排序
retrieve:
显示商品详情
"""
queryset = Goods.objects.all() # 使用get_queryset函数,依赖queryset的值
serializer_class = GoodsSerializer
pagination_class = GoodsPagination
filter_backends = (DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter,) # 将过滤器后端添加到单个视图或视图集
filterset_class = GoodsFilter
# authentication_classes = (TokenAuthentication, ) # 只在本视图中验证Token
search_fields = ('name', 'goods_desc', 'category__name') # 搜索字段
ordering_fields = ('click_num', 'sold_num', 'shop_price') # 排序
def retrieve(self, request, *args, **kwargs):
# 增加点击数
instance = self.get_object()
instance.click_num += 1
instance.save()
serializer = self.get_serializer(instance)
return Response(serializer.data)
只需要再继承CacheResponseMixin
,缓存就生效了。现在重启服务器,使浏览器缓存失效(因为我们第一次进入该页面,已经将数据放入缓存了,重启服务器,内存中就没了,需要重新到后台获取),然后访问 http://127.0.0.1:8000/goods/ 按F12点击Network查看加载时间。
可以看到首次访问需要437ms
再次刷新该页面,第二次以及后面多次访问基本就是13s
当数据量很多的时候,缓存效果就非常明显了。
但是缓存也需要设置一个过期时间,否则,每次都从缓存中获取数据,一旦数据修改之后,就不能获取更新的数据了。所以需要设置过期时间,在一段时间后,将从数据库中查询新的数据并更新缓存数据。
配置缓存过期时间
文档中 http://chibisov.github.io/drf-extensions/docs/#timeout 也有相关的配置说明
修改 DjangoOnlineFreshSupermarket/settings.py 添加配置
# drf-extensions配置
REST_FRAMEWORK_EXTENSIONS = {
'DEFAULT_CACHE_RESPONSE_TIMEOUT': 60 * 10 # 缓存全局过期时间(60 * 10 表示10分钟)
}
单位为秒,可以将它设置成5s做下测试,看是否5s后过期,加载时间变长。
对于总结:对于公共数据,大家都可以访问的,以及不经常变动的数据可以增加缓存,以减少请求时间。另外在 drf-extensions 中也有很多配置,可以根据自己的需求自定义。
DRF配置redis缓存后端
redis作为backend来缓存数据
对于 http://127.0.0.1:8000/goods/ 和 http://127.0.0.1:8000/goods/?format=json 以及不同的过滤参数,返回结果应该是不一样的。
访问 https://django-redis-chs.readthedocs.io/zh_CN/latest/ 可以看到 django-redis 的使用方法
安装django-redis
pip install django-redis
配置redis缓存后端
修改 DjangoOnlineFreshSupermarket/settings.py 增加配置
# 配置 django-redis做缓存后端
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://127.0.0.1:6379/1",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
# "PASSWORD": "blog.starmeow.cn" # 如果redis服务器设置了密码,配置成自己的密码
}
}
}
当访问 http://127.0.0.1:8000/goods/
刷新redis客户端可以看到缓存的键和数据
访问 http://127.0.0.1:8000/goods/?format=json 又生成了一个键
同样 http://127.0.0.1:8000/goods/?page=2 又会生成一个键,过期时间为 drf-extensions 中配置REST_FRAMEWORK_EXTENSIONS
过期时间。
也就是说,在视图中配置CacheResponseMixin
缓存后,可以使用redis作为缓存后端来存储数据。且针对不同的url以不同的参数,都会生成不同的键值,不同的键缓存不同的内存。如果没有配置CacheResponseMixin
缓存,访问这些URL,是不会生成redis键值的。
Redis客户端工具可以访问 https://github.com/qishibo/AnotherRedisDesktopManager/releases 下载使用。
DRF的throttle设置API的访问速率
接口访问速率过快,会导致其它业务受影响,网站打不开,服务器压力过大的请况。
这个限速时DRF自带的功能 https://www.django-rest-framework.org/api-guide/throttling/ ,直接拿来使用即可。
设置全局限制策略
可以使用DEFAULT_THROTTLE_CLASSES
和DEFAULT_THROTTLE_RATES
设置全局设置默认限制策略。
修改 DjangoOnlineFreshSupermarket/settings.py 在REST_FRAMEWORK
添加配置
# DRF配置
REST_FRAMEWORK = {
# 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
# 'PAGE_SIZE': 5,
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.BasicAuthentication',
'rest_framework.authentication.SessionAuthentication', # 上面两个用于DRF基本验证
# 'rest_framework.authentication.TokenAuthentication', # TokenAuthentication,取消全局token,放在视图中进行
# 'rest_framework_simplejwt.authentication.JWTAuthentication', # djangorestframework_simplejwt JWT认证
),
# throttle对接口访问限速
'DEFAULT_THROTTLE_CLASSES': [
'rest_framework.throttling.AnonRateThrottle', # 用户未登录请求限速,通过IP地址判断
'rest_framework.throttling.UserRateThrottle' # 用户登陆后请求限速,通过token判断
],
'DEFAULT_THROTTLE_RATES': {
'anon': '60/minute', # 限制所有匿名未认证用户,使用IP区分用户。使用DEFAULT_THROTTLE_RATES['anon'] 来设置频次
'user': '200/minute' # 限制认证用户,使用User id 来区分。使用DEFAULT_THROTTLE_RATES['user'] 来设置频次
}
}
DEFAULT_THROTTLE_RATES
中使用的速度单位包括second
, minute
, hour
或者 day
作为限流时间。
添加上面配置后,可以将配置修改'anon': '6/minute',
,也就是每分钟匿名只允许访问6次。
当访问上面的所有接口,包含Api Root,总和超过6次之后,就会有限速提示
这是所有API调用次数总数计算,并非单个API都有指定的次数。
单个API限速
还可以使用基于APIView类的视图在每个视图或每个视图集的基础上设置限制策略。
首先配置限速范围,修改 DjangoOnlineFreshSupermarket/settings.py ,注释掉全局限速,增加自定义权限
# DRF配置
REST_FRAMEWORK = {
# 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
# 'PAGE_SIZE': 5,
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.BasicAuthentication',
'rest_framework.authentication.SessionAuthentication', # 上面两个用于DRF基本验证
# 'rest_framework.authentication.TokenAuthentication', # TokenAuthentication,取消全局token,放在视图中进行
# 'rest_framework_simplejwt.authentication.JWTAuthentication', # djangorestframework_simplejwt JWT认证
),
# throttle对接口访问限速
'DEFAULT_THROTTLE_CLASSES': [
# 'rest_framework.throttling.AnonRateThrottle', # 用户未登录请求限速,通过IP地址判断
# 'rest_framework.throttling.UserRateThrottle' # 用户登陆后请求限速,通过token判断
'rest_framework.throttling.ScopedRateThrottle', # 限制用户对于每个视图的访问频次,使用ip或user id。
],
'DEFAULT_THROTTLE_RATES': {
# 'anon': '60/minute', # 限制所有匿名未认证用户,使用IP区分用户。使用DEFAULT_THROTTLE_RATES['anon'] 来设置频次
# 'user': '200/minute' # 限制认证用户,使用User id 来区分。使用DEFAULT_THROTTLE_RATES['user'] 来设置频次
'goods_list': '600/minute'
}
}
就可以在视图中添加throttle_scope = 'goods_list'
来达到限速目的。可以将'goods_list': '600/minute'
修改小一点测试。
例如可以给 apps/goods/views.py 中的GoodsListViewSet
配置限速
from rest_framework.throttling import UserRateThrottle, AnonRateThrottle
class GoodsListViewSet(CacheResponseMixin, mixins.ListModelMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet):
"""
list:
显示商品列表,分页、过滤、搜索、排序
retrieve:
显示商品详情
"""
queryset = Goods.objects.all() # 使用get_queryset函数,依赖queryset的值
serializer_class = GoodsSerializer
pagination_class = GoodsPagination
filter_backends = (DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter,) # 将过滤器后端添加到单个视图或视图集
filterset_class = GoodsFilter
# authentication_classes = (TokenAuthentication, ) # 只在本视图中验证Token
search_fields = ('name', 'goods_desc', 'category__name') # 搜索字段
ordering_fields = ('click_num', 'sold_num', 'shop_price') # 排序
# throttle_classes = [UserRateThrottle, AnonRateThrottle] # DRF默认限速类,可以仿照写自己的限速类
throttle_scope = 'goods_list'
def retrieve(self, request, *args, **kwargs):
# 增加点击数
instance = self.get_object()
instance.click_num += 1
instance.save()
serializer = self.get_serializer(instance)
return Response(serializer.data)
现在访问 http://127.0.0.1:8000/goods/ 超限后就会提示 请求超过了限速。而其他API则不受影响。
另外可以仿照 UserRateThrottle, AnonRateThrottle
写自己的限速类