常量定义及构造函数
class TTLCache(object):
NOT_FOUND = None
def __init__(self):
self.datas = dict()
self.expires = dict()
- self.datas:记录key to value映射关系的dict
- self.expires:记录key to expire_ts映射关系的dict
- NOT_FOUND:get操作在key不存在时的返回值
判断给定key是否存在对应的value、是否该key是否仍在存活期内
def is_key_valid_on_existence(self, key):
return key in self.datas
def is_key_valid_on_liveness(self, key, now):
return key in self.expires and now < self.expires[key]
- is_key_valid_on_existence:仅从“存在性”的角度来看,key是否在cache中
- is_key_valid_on_liveness:仅从“存活性”的角度来看,key是否在cache中
这两个函数是get操作的辅助判断函数
get、set操作
def get(self, key):
now = curr_ts()
if self.is_key_valid_on_existence(key) and self.is_key_valid_on_liveness(key, now):
return self.datas[key], True
return TTLCache.NOT_FOUND, False
def set(self, key, value, ttl=86400):
now = curr_ts()
self.datas[key] = value
self.expires[key] = now + ttl
- get:类似golang的map,此函数的返回值被设计为“多返回值”。
- 第一个参数是value,第二个参数是代表“value是否有效”的bool类型标记
- 当value不存在于cache中时,value被设置为常量NOT_FOUND
- “value存在于cache中”的判断条件是is_key_valid_on_existence、is_key_valid_on_liveness都返回True
- set:分别把value和expire值赋值到self.datas和self.expires两个dict中
自动清理过期key
def gc_main(self):
from time import sleep
while True:
sleep(5)
now = curr_ts()
keys = list(self.datas.keys())
for key in keys:
exp = self.expires.get(key)
if exp > now:
continue
else:
del self.datas[key]
del self.expires[key]
def start_gc(self):
from threading import Thread
thr = Thread(target=self.gc_main)
thr.daemon = True
thr.start()
- start_gc:启动gc线程的函数
- gc线程的主函数为gc_main
- gc线程作为辅助线程,设置为daemon,使得gc线程不掺和到“判断进程是否还有非守护线程,如果没有的话进程就exit了”的逻辑中去
- gc_main:gc线程主函数
- 每隔5秒执行一次gc逻辑
- keys = list(self.datas.keys()):先取出所有key组成的清单(如果一边迭代dict一边删除dict里的key,会报错。所以要提前取一次全量key的清单)
- exp = self.expires.get(key):取出key的过期时间
- 如果exp>now,说明过期时间在当前时间的将来,即“未过期”
- 如果not exp > now,说明过期时间在当前时间的过去,即“已过期”。此时需要对self.datas、self.expires中的key都执行删除操作
最后还要在构造函数中加入启动gc线程的操作
class TTLCache(object):
......
def __init__(self):
......
self.start_gc()
完整代码如下
import time
def curr_ts():
return time.time()
class TTLCache(object):
NOT_FOUND = None
def __init__(self):
self.datas = dict()
self.expires = dict()
self.start_gc()
def get(self, key):
now = curr_ts()
if self.is_key_valid_on_existence(key) and self.is_key_valid_on_liveness(key, now):
return self.datas[key], True
return TTLCache.NOT_FOUND, False
def set(self, key, value, ttl=86400):
now = curr_ts()
self.datas[key] = value
self.expires[key] = now + ttl
def is_key_valid_on_existence(self, key):
return key in self.datas
def is_key_valid_on_liveness(self, key, now):
return key in self.expires and now < self.expires[key]
def gc_main(self):
from time import sleep
while True:
sleep(5)
now = curr_ts()
keys = list(self.datas.keys())
for key in keys:
exp = self.expires.get(key)
if exp > now:
continue
else:
del self.datas[key]
del self.expires[key]
def start_gc(self):
from threading import Thread
thr = Thread(target=self.gc_main)
thr.daemon = True
thr.start()
if __name__ == '__main__':
cache = TTLCache()
cache.set('name', 'John', 5)
value_valid = cache.get('name')
print(value_valid)
from time import sleep
sleep(6)
value_valid = cache.get('name')
print(value_valid)
print(cache.datas, cache.expires)