最近对 DRF 的接口进行监控,发现部分高频访问的接口性能不佳,响应比较慢,于是想通过缓存将接口性能优化。

常用的解决方案如下:

参考:How to cache Django Rest Framework API calls

可行的方案有很多,可以通过组件实现也可以通过 django 内置的装饰器来实现。这里以 django-rest-framework-cache 为例。

django-rest-framework-cache

django-rest-framework-cache 是通过重写 Serializerto_representation 方法来实现缓存的,所以它是一个嵌入到序列化层缓存数据的工具。django-rest-framework-cache 安装配置比较简单,能够快速上手。

安装

$ pip install rest-framework-cache

配置

缓存配置可以参考 Django 官方文档,Django 原生默认支持 Memcached,不过我们需要先安装 python-memcachedmemcached 等软件包。

$ pip install python-memcached
$ sudo yum install -y memcached
$ cat /etc/sysconfig/memcached
PORT="11211"
USER="memcached"
MAXCONN="2000"
CACHESIZE="1024"
OPTIONS=""
$ systemctl start memcached
$ systemctl enable memcached

settings.py

INSTALLED_APPS = (
...
'rest_framework_cache',
)
# 缓存源
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
'LOCATION': '127.0.0.1:11211',
},
'rest_backend': {
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
'LOCATION': 'unique-snowflake',
}
}
REST_FRAMEWORK_CACHE = {
'DEFAULT_CACHE_BACKEND': 'rest_backend',
}
# 全局超时时间
REST_FRAMEWORK_CACHE = {
'DEFAULT_CACHE_TIMEOUT': 86400, # Default is 1 day
}

urls.py

from rest_framework_cache.registry import cache_registry
cache_registry.autodiscover()

使用

django-rest-framework-cache 是在序列化层进行缓存,所以将要缓存的序列化类注册到缓存中,同时需要使序列化类继承于 CachedSerializerMixin

官方使用示例如下:

from rest_framework import serializers
# You must import the CachedSerializerMixin and cache_registry
from rest_framework_cache.serializers import CachedSerializerMixin
from rest_framework_cache.registry import cache_registry
from .models import Comment
class CommentSerializer(serializers.ModelSerializer, CachedSerializerMixin):
class Meta:
model = Comment
cache_registry.register(CommentSerializer)

按照官方配置会报错:

TypeError: Error when calling the metaclass bases
Cannot create a consistent method resolution
order (MRO) for bases CachedSerializerMixin, ModelSerializer

stackoverflow 上给的解答是因为 Python 类继承的 MRO 算法导致。 由于 CachedSerializerMixin 是继承于 serializers.ModelSerializer, 必须先继承底层的类,再继承高层的类。这里因为 serializers.ModelSerializer 出现在 CachedSerializerMixin 之前导致报错。有兴趣可以了解一下 Python 多重继承的 C3 线性化算法与 MRO

参考:TypeError: Cannot create a consistent method resolution order (MRO)

将两者交换顺序即可:

class CommentSerializer(CachedSerializerMixin, serializers.ModelSerializer):

django-rest-framework-cache 存在一个问题就是对于嵌套的外键和多对多的模型,数据修改之后不会生效,因为缓存的是 Serializer 信息,只在该 Serializer 对应的 Model 实例被修改后才会刷新。不过这个问题已经有人改写了插件 django-rest-framework-cache

参考:rest_framework_cache 优化接口访问速度