Redis获取并设置装饰器

时间:2016-08-26 06:53:55

标签: python caching redis decorator

目前正在开发python和redis。我将Flask作为我的框架并开发蓝图。

考虑使用redis为我的API实现缓存,我尝试了 Flask-Cache redis-simple-cache 。 即使更改函数的参数,下行Flask-Cache也会保存该函数。它每个功能只保存一次。 根据{{​​3}},它将密钥保存为SimpleCache-<key name>,这在我的最后是不可取的。

所以我的问题是,如何创建一个装饰器来保存和检索或检查特定键是否存在密钥。 我知道保存装饰器是可能的。但是检索或检查装饰可能吗?如果我错了,请纠正我。谢谢。

2 个答案:

答案 0 :(得分:0)

你可以使用这个cache decorator,你创建的缓存对象必须是一个烧瓶缓存对象而不是django,即应该支持cache.get和cache.set方法,这是非常灵活的基于你如何想要创建缓存键,即

  • 基于传递给方法的参数
  • 在什么情况下缓存/不缓存结果
  • 即使更改了kwarg顺序,也使用相同的缓存,即my_method(a = 1,b = 2)和my_method(b = 2,a = 1)调用的缓存相同。

&#34;

__author__ = 'Dhruv Pathak'


import cPickle
import logging
import time
from functools import wraps
from django.conf import settings
import traceback

"""following imports are from datautils.py in this repo, datautils
also contains many other useful data utility methods/classes
"""
from datautils import mDict, mList, get_nested_ordered_dict, nested_path_get

"""following import is specific to django framework, and can be altered
based on what type of caching library your code uses"""
from django.core.cache import cache, caches


logger = logging.getLogger(__name__)

def cache_result(cache_key=None, cache_kwarg_keys=None, seconds=900, cache_filter=lambda x: True, cache_setup = "default"):
    def set_cache(f):
        @wraps(f)
        def x(*args, **kwargs):
            if settings.USE_CACHE is not True:
                result = f(*args, **kwargs)
                return result
            try:
                # cache_conn should be a cache object supporting get,set methods
                # can be from python-memcached, pylibmc, django, django-redis-cache, django-redis etc
                cache_conn = caches[cache_setup]
            except Exception, e:
                result = f(*args, **kwargs)
                return result
            final_cache_key = generate_cache_key_for_method(f, kwargs, args, cache_kwarg_keys, cache_key)
            try:
                result = cache_conn.get(final_cache_key)
            except Exception, e:
                result = None
                if settings.DEBUG is True:
                    raise
                else:
                    logger.exception("Cache get failed,k::{0},ex::{1},ex::{2}".format(final_cache_key, str(e), traceback.format_exc()))

            if result is not None and cache_filter(result) is False:
                result = None
                logger.debug("Cache had invalid result:{0},not returned".format(result))

            if result is None: # result not in cache
                result = f(*args, **kwargs)
                if isinstance(result, (mDict, mList)):
                    result.ot = int(time.time())
                if cache_filter(result) is True:
                    try:
                        cache_conn.set(final_cache_key, result, seconds)
                    except Exception, e:
                        if settings.DEBUG is True:
                            raise
                        else:
                            logger.exception("Cache set failed,k::{0},ex::{1},ex::{2},dt::{3}".format(final_cache_key, str(e), traceback.format_exc(), str(result)[0:100],))
                #else:
                #    logger.debug("Result :{0} failed,not cached".format(result))

            else: # result was from cache
                if isinstance(result, (mDict, mList)):
                    result.src = "CACHE_{0}".format(cache_setup)
            return result
        return x
    return set_cache


def generate_cache_key_for_method(method, method_kwargs, method_args, cache_kwarg_keys=None, cache_key=None):
    if cache_key is None:
        if cache_kwarg_keys is not None and len(cache_kwarg_keys) > 0:
            if len(method_args) > 0:
                raise Exception("cache_kwarg_keys mode needs set kwargs,args should be empty")

            method_kwargs = get_nested_ordered_dict(method_kwargs)
            cache_kwarg_values = [nested_path_get(method_kwargs, path_str=kwarg_key, strict=False) for kwarg_key in cache_kwarg_keys]
            if any([kwarg_value is not None for kwarg_value in cache_kwarg_values]) is False:
                raise Exception("all cache kwarg keys values are not set")

            final_cache_key = "{0}::{1}::{2}".format(str(method.__module__), str(method.__name__), hash(cPickle.dumps(cache_kwarg_values)))
        else:
            final_cache_key = "{0}::{1}".format(str(method.__module__), str(method.__name__))
            final_cache_key += "::{0}".format(str(hash(cPickle.dumps(method_args, 0)))) if len(method_args) > 0 else ''
            final_cache_key += "::{0}".format(str(hash(cPickle.dumps(method_kwargs, 0)))) if len(method_kwargs) > 0 else ''
    else:
        final_cache_key = "{0}::{1}::{2}".format(method.__module__, method.__name__, cache_key)

    return final_cache_key

在同一个仓库中从此file导入2-3个实用程序方法,您可以将它们放在同一个文件中。

答案 1 :(得分:0)

您似乎没有非常仔细地阅读Flask-Cache documentation。缓存忽略参数,缓存键可自定义。项目提供的装饰器已经为您提供了所需的功能。

来自Caching View Functions section

  

默认情况下,此装饰器将request.path用于cache_key

因此默认缓存键为request.path,但您可以指定其他键。由于Flask view 函数从路径元素中获取其参数,因此默认的request.path是一个很好的密钥。

来自@cached() decorator documentation

  

cached(timeout=None, key_prefix='view/%s', unless=None)

     

默认情况下,缓存键为 view / request.path 。通过更改 key_prefix ,您可以将此装饰器用于任何功能。如果令牌%s 位于 key_prefix 中,则它将替换为 request.path 。通过更改 key_prefix ,您可以将此装饰器用于任何功能。

  

key_prefix - [...] 可以选择是一个不带参数的可调用对象,但返回一个将用作cache_key的字符串。

所以你可以将key_prefix设置为一个函数,并且它将被调用(不带参数)来生成密钥。

此外:

  

返回的修饰函数现在分配了三个函数属性。这些属性是可读/可写的:

     

[...]

     

make_cache_key
  用于生成cache_key的函数。

此函数传递视图函数传递的相同参数。总而言之,这允许您生成所需的任何缓存键;使用key_prefix并从requestg或其他来源提取更多信息,或分配给view_function.make_cache_key并访问视图功能接收的相同参数。

然后是@memoize() decorator

  

memoize(timeout=None, make_name=None, unless=None)

     

使用它来缓存函数的结果,并在缓存键中考虑其参数。

因此,这个装饰器纯粹基于传递给函数的参数来缓存返回值。它也支持make_cache_key功能。

我已经使用两个装饰器来制作Google App Engine Flask项目规模,以便为CMS支持的网站每月数百万次浏览,将结果存储在Google memcached结构中。使用Redis执行此操作只需要设置配置选项。