美文网首页Django+Vue生鲜电商python
【Vue+DRF生鲜电商】33.数据缓存、接口访问限速功能

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

作者: 吾星喵 | 来源:发表于2019-08-14 14:12 被阅读3次

    专题: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客户端可以看到缓存的键和数据

    BLOG_20190814_140859_75

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

    BLOG_20190814_140854_34

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

    BLOG_20190814_140845_18

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

    BLOG_20190814_140836_94

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

    相关文章

      网友评论

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

        本文链接:https://www.haomeiwen.com/subject/zrugjctx.html