SqlAlchemy做数据库缓存 -- dogpile.cache

dogpile是一种控制结构,它允许选择单个执行线程作为某些资源的“创建者”,同时允许其他执行线程在创建过程中引用此资源的先前版本。

dogpile.cache是​​一个缓存API,它提供了一个通用接口来缓存任何种类的后端。

首先安装dogpile:pip install dogpile.cache
现在有很多有名的key/value服务比如说redis Memcached,他们都很适合用于缓存,特别是Memcached。考虑到缓存系统,dogpile.cache提供了针对该系统的特定Python API的接口。

dogpile.cache配置包含以下组件:

  1. region: 是一个CacheRegion的实例,可以被看做是front end
  2. backend: 是一个CacheBackend的实例,描述了数据是怎么被储存的。这个接口只提供了get(),set(),delete()。用于特定CacheRegion的实际CacheBackend类型由用于与缓存通信的底层API,比如pylibmc(Memcached的python客户端)确定。
  3. Value generation functions: 这些是用户定义的函数,用于生成要放入缓存的数据。虽然godpile.cache提供了将数据放入缓存的常用set方法,但是通常使用方法是get。传递一个创建函数,当且仅当需要时,它将用于生成新值,这种“get-or-create”模式是“Dogpile”系统的全部关键。

初步使用:
dogpile.cache包含一个Pylibmc后端

from dogpile.cache import make_region

# 这里创建了一个CacheRegion实例对象
Region = make_region()
# 对CacheRegion对象配置了backend
region = Region.configure(
    'dogpile.cache.pylibmc',  # 这是name of backend,是需要的唯一arguement
    expiration_time = 3600,
    arguments = {
        'url': ["127.0.0.1"],  # 这里是传递Memcached server的URL
    }
)

@region.cache_on_arguments()
def load_user_info(user_id):
    return some_database.lookup_user_by_id(user_id)

所以最后都是使用region作为装饰器


make_region()

make_region()函数会直接调用CacheRegion的结构
该函数有以下参数:

  • name: 可选,region的名字,可以用.name获取,在config file中配置region时会有用
  • function_key_generator: 可选,用于使用CacheRegion.cache_on_arguments() 方法,这个函数需要有两个level。 一个是给出data creation function,然后需要返回一个新function用于基于给定的arguement生成key。
    def my_key_generator(namespace, fn, **kw):
        fname = fn.__name__
        def generate_key(*arg):
            return namespace + "_" + fname + "_".join(str(s) for s in arg)
        return generate_key
    
    
    region = make_region(
        function_key_generator = my_key_generator
    ).configure(
        "dogpile.cache.dbm", # python的database manager,python本身有很多dbm库,但是dbm是一个方便选择,不用选择特定的dbm模块,它可以自己选择最适合的
        expiration_time=300,
        arguments={
            "filename":"file.dbm"
        }
    )
    
    使用装饰器:
    @my_region.cache_on_arguments(namespace=('x', 'y'))
    def my_function(a, b, **kw):
        return my_data()
    
  • function_multi_key_generator:可选,用于使用CacheRegion.cache_multi_on_arguments()方法,Generated function会返回一个key列表。
    def my_multi_key_generator(namespace, fn, **kw):
    namespace = fn.__name__ + (namespace or '')
    
    def generate_keys(*args):
        return [namespace + ':' + str(a) for a in args]
    
    return generate_keys
    
  • key_mangler: 可选,比较典型的是SHA1,sha1_mangle_key() 方法会对所有的key做hash
  • async_creation_runner: 它将通过互斥锁并在完成时负责释放该互斥锁
    import threading
    def async_creation_runner(cache, somekey, creator, mutex):
        def runner():
            try:
                value = creator()
                cache.set(somekey, value)
            finally:
                mutex.release()
    
        thread = threading.Thread(target=runner)
        thread.start()
    
    
    region = make_region(
        async_creation_runner=async_creation_runner,
    ).configure(
        'dogpile.cache.memcached',
        expiration_time=5,
        arguments={
            'url': '127.0.0.1:11211',
            'distributed_lock': True,
        }
    )
    


CacheRegion.configure()

根据上面一步,当我们有了CacheRegion的时候,CacheRegion.cache_on_arguments() 方法就可以用来当做装饰器了,但是这个缓存现在还不能用,必须经过CacheRegion.configure()配置了backend等信息才能使用

CacheRegion.configure() 有以下几个参数:

  • backend: CacheBackend的名字,必须项
  • expiration_time: 在上次value creation function调用之后,经过这段时间,才会调用下一次value creation function
  • arguments: 指定backend的地址
  • wrap: ProxyBackend类和/或实例的列表,每个类和/或实例用于包装原始backend,以便可以应用自定义功能扩充。
  • replace_existing_backend: 设置为True的话,已经存在的cache backend会被替换,如果不设置,对存在的backend进行配置会报错。
  • region_invalidator: 重写默认的无效策略
  • CacheRegion.configure_from_config():使用configuration dictionary和里面的前缀批量配置backend:
    local_region = make_region()
    memcached_region = make_region()
    # 上面CacheRegion已经创建好,但是还没有配置
    myconfig = {
        "cache.local.backend":"dogpile.cache.dbm",
        "cache.local.arguments.filename":"/path/to/dbmfile.dbm",
        "cache.memcached.backend":"dogpile.cache.pylibmc",
        "cache.memcached.arguments.url":"127.0.0.1, 10.0.0.1",
    }
    local_region.configure_from_config(myconfig, "cache.local.")
    memcached_region.configure_from_config(myconfig,"cache.memcached.")
    


如何使用Region

上面对CacheRegion该配置的已经配置好了,这玩意儿相当于我们访问cache的前端接口,它包含下面的方法:

首先介绍invalidate()方法:
默认的invalidation system就是为value设置当前时间为最小的创建时间,任何检索值的创建时间早于这个timestamp就会被判定为过期,它不会对缓存中的值产生影响。这个timestamp被进程储存在本地内存,所以不会影响其他进程。但是如果这个timestamp被储存在缓存中,各进程都会被同一个timestamp影响,那么就需要制定一个custom RegionInvalidationStrategy.

这个方法可以有hard和soft两个选项,如果是hard,那么CacheRegion.get_or_create()会让所有的getters都等待value重新生成,如果是soft,那么随后的getters会获取old value直到新的value生成。


CacheRegion.get(key, expiration_time=None, ignore_expiration=False)

key: 根据key从Cache里面获取value,如果获取不到,该方法就会返回一个token:NO_VALUE。

expiration_time: 这个过期时间,要么是在region配置的时候设定好了,要么是现在调用的时候传递的参数,它会比较检索值的创建时间和现在时间(time.time()),如果过期了,这个缓存值会被忽略并且返回NO_VALUE。

ignore_expiration: 如果我们这样设置参数ignore_expiration=True, 那么会绕过这个过期时间检测


CacheRegion.get_or_create(key, creator, expiration_time=None, should_cache_fn=None, creator_args=None)

根据给定的key在缓存中获取对应的value,如果这个值不存在或者过期了,那么给定的creation function可能会创建新的值并且保存。

并发锁:
是否能使用这个creation function取决于是否能获取这个dogpile lock,如果不能,证明此时正有另一个线程或者进程在使用creation function为这个key创建值。
当这个dogpile不能获取的时候:
如果previous value不存在,那么这个方法就会block并且等待直到这个锁被释放,并且有一个新的值available。
如果previous value存在,那么会直接被返回。

如果invalidate()方法被called,如果检索值的timestamp比invalidation timestamp老,那么该值不管如何都不会被返回,该方法会尝试获取dogpile lock去生成新的值,或者等待lock释放然后返回新的值

key:检索的key
creator:用于创建新的值的函数
creator_args: 向创建函数传递参数 tuple
expiration_time: 会重写配置region时设置的过期时间,如果不需要过期时间,设置为-1
should_cache_fn:这个方法会接受creation function的返回值,然后根据返回True或者False判断新生成的值是否需要被保存到cache中:

def dont_cache_none(value):
    return value is not None
    # 如果这里返回False,那么该值不会被缓存

value = region.get_or_create("some key",
                    create_value,  # 这是creation function
                    should_cache_fn=dont_cache_none)

CacheRegion.set(key, value)

在缓存中为特定的key设置value


CacheRegion.delete(key)

缓存中删除特定的键值对,该操作幂等


CacheRegion.cache_on_arguments(namespace=None, expiration_time=None, should_cache_fn=None, to_str=<class ‘str’>, function_key_generator=None)

这是一个装饰器函数将creation function创建的键值对储存到缓存中,这个装饰器会内在的调用CacheRegion.get_or_create()方法去获取缓存然后看是否需要调用该函数。

@someregion.cache_on_arguments()
def generate_something(x, y):
    return somedatabase.query(x, y)

这个被装饰的方法可以现在被调用了,数据会从cache里面获取,直到新的value被需要
result = generate_something(5, 6)

这个方法有一个属性为invalidate(),可以验证value的timestamp:
generate_something.invalidate(5, 6)

这个方法还可以直接使用set(),这样会直接储存这个给定的value,而不需要调用这个被装饰的函数:
generate_something.set(3, 5, 6)

这个方法有属性refresh(),该方法会调用被装饰函数去产生新的value放入缓存:
newvalue = generate_something.refresh(5, 6)

这个方法有属性original(),它会调用被装饰函数而不是获取缓存中的数据:
newvalue = generate_something.original(5, 6)

这个方法有属性get(),它会返回缓存中的值,或者返回NO_VALUE:
value = generate_something.get(5, 6)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章