dogpile是一种控制结构,它允许选择单个执行线程作为某些资源的“创建者”,同时允许其他执行线程在创建过程中引用此资源的先前版本。
dogpile.cache是一个缓存API,它提供了一个通用接口来缓存任何种类的后端。
首先安装dogpile:pip install dogpile.cache
现在有很多有名的key/value服务比如说redis Memcached,他们都很适合用于缓存,特别是Memcached。考虑到缓存系统,dogpile.cache提供了针对该系统的特定Python API的接口。
dogpile.cache配置包含以下组件:
- region: 是一个CacheRegion的实例,可以被看做是front end
- backend: 是一个CacheBackend的实例,描述了数据是怎么被储存的。这个接口只提供了get(),set(),delete()。用于特定CacheRegion的实际CacheBackend类型由用于与缓存通信的底层API,比如pylibmc(Memcached的python客户端)确定。
- 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)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
所以最后都是使用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" } )
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
@my_region.cache_on_arguments(namespace=('x', 'y')) def my_function(a, b, **kw): return my_data()
- 1
- 2
- 3
- 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
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 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, } )
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
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.")
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
如何使用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)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
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)
- 1
- 2
- 3
这个被装饰的方法可以现在被调用了,数据会从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)