【Vue+DRF生鲜电商】33.数据缓存、接口访问限速功能

专题: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的 retrievelist方法很常见。 这就是CacheResponseMixin存在的原因。 只需将其混合到viewset实现中,这些方法将使用REST_FRAMEWORK_EXTENSIONS设置中定义的函数:

  • “DEFAULT_OBJECT_CACHE_KEY_FUNC” 用于 retrieve 方法
  • “DEFAULT_LIST_CACHE_KEY_FUNC” 用于 list 方法

默认情况下,这些函数使用DefaultKeyConstructor并对其进行扩展:

  • 使用RetrieveSqlQueryKeyBit获取 “DEFAULT_OBJECT_CACHE_KEY_FUNC”
  • 使用ListSqlQueryKeyBitPaginationKeyBit“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

BLOG_20190814_140915_74

再次刷新该页面,第二次以及后面多次访问基本就是13s

BLOG_20190814_140909_18

当数据量很多的时候,缓存效果就非常明显了。

但是缓存也需要设置一个过期时间,否则,每次都从缓存中获取数据,一旦数据修改之后,就不能获取更新的数据了。所以需要设置过期时间,在一段时间后,将从数据库中查询新的数据并更新缓存数据。

配置缓存过期时间

文档中 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客户端可以看到缓存的键和数据

【Vue+DRF生鲜电商】33.数据缓存、接口访问限速功能_第1张图片

访问 http://127.0.0.1:8000/goods/?format=json 又生成了一个键

【Vue+DRF生鲜电商】33.数据缓存、接口访问限速功能_第2张图片

同样 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_CLASSESDEFAULT_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次。

【Vue+DRF生鲜电商】33.数据缓存、接口访问限速功能_第3张图片

当访问上面的所有接口,包含Api Root,总和超过6次之后,就会有限速提示

【Vue+DRF生鲜电商】33.数据缓存、接口访问限速功能_第4张图片

这是所有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写自己的限速类

你可能感兴趣的:(Web开发)