我正在使用Redis在Django Rest Ramework API中缓存一些视图。假设我有以下观点:
from django.utils import timezone
from django.utils.decorators import method_decorator
from django.views.decorators.cache import cache_page
from rest_framework.response import Response
from rest_framework.views import APIView
class MyView(APIView):
@method_decorator(cache_page(60 * 15))
def get(self, request):
return Response({"timestamp": timezone.now()})
当Redis启动并运行时,一切都很好。然而,我们的Redis实例最近出现了间歇性中断(大喊Heroku Redis(,这导致所有使用Redis缓存的端点崩溃,从而导致任何使用cache_page
装饰器的端点崩溃并返回500内部服务器错误。
我想实现一种故障转移机制,该机制只需忽略缓存,并在引发redis.ConnectionError
时成功返回响应。以下是我目前所拥有的:
def cache_page_with_failover(timeout, *, cache=None, key_prefix=None):
def decorator(view_func):
@wraps(view_func)
def wrapper(*args, **kwargs):
try:
return cache_page(timeout, cache=cache, key_prefix=key_prefix)(
view_func
)(*args, **kwargs)
except redis.ConnectionError:
return view_func(*args, **kwargs)
return wrapper
return decorator
用法:
class MyView(APIView):
@method_decorator(cache_page_with_failover(60 * 15))
def get(self, request):
return Response({"timestamp": timezone.now()})
这很有效,但它非常冗长,我觉得我在复制cache_page
签名。有没有更干净、更优雅的写法?
直接接受cache_page
:返回的decorator
def with_redis_failover(cache_decorator):
def decorator(view_func):
cache_decorated_view_func = cache_decorator(view_func)
@wraps(view_func)
def wrapper(*args, **kwargs):
try:
return cache_decorated_view_func(*args, **kwargs)
except redis.ConnectionError:
return view_func(*args, **kwargs)
return wrapper
return decorator
用法:
# @method_decorator(cache_page_with_failover(60 * 15))
@method_decorator(with_redis_failover(cache_page(60 * 15)))
这可以概括为:
def with_failover(decorator, exception_type):
def _decorator(view_func):
decorated_view_func = decorator(view_func)
@wraps(view_func)
def wrapper(*args, **kwargs):
try:
return decorated_view_func(*args, **kwargs)
except exception_type:
return view_func(*args, **kwargs)
return wrapper
return _decorator
用法:
@method_decorator(with_failover(cache_page(60 * 15), redis.ConnectionError))
with_redis_failover = partial(with_failover, exception_type=redis.ConnectionError)
@method_decorator(with_redis_failover(cache_page(60 * 15)))