class CacheModule: def __init__(self, name, max_size=128, from_remote=None): ''' :param max_size: 最大容量 :param from_remote: 查询数据函数,返回值必须是一个dict :param ttl: 有效时间,单位:秒 ''' self.local_cache = LRU(max_size=max_size) self.from_remote = from_remote self.empty = {} self.name = name def get_all(self, keys: list) -> dict: result, missing_keys = {}, [] for key in keys: val = self.local_cache.get(key) if val is not None: if val != self.empty: result[key] = val else: missing_keys.append(key) # 缓存未命中 if len(missing_keys) > 0: missing_dict = self.reload(missing_keys) result = {**result, **missing_dict} return result def remove(self, keys: list): for key in keys: self.local_cache.__delitem__(key) def statistics(self): hitRateAsString = '%.3f' % ( self.local_cache.hit_count / (self.local_cache.hit_count + self.local_cache.miss_count)) return { "name": self.name, "hit_count": self.local_cache.hit_count, "miss_count": self.local_cache.miss_count, "size": len(self.local_cache.keys()), "hitRateAsString": hitRateAsString + "%", } def reload(self, keys: list) -> dict: d = {} missing_vals = self.from_remote(keys) # 不存在的值,设置为空 if missing_vals: for key, val in missing_vals.items(): self.local_cache.setdefault(key, val) d[key] = val for missing_key in keys: self.local_cache.setdefault(missing_key, self.empty) return d
def test_lru(): lru = LRU(max_size=1) lru['hi'] = 0 lru['bye'] = 1 assert len(lru) == 1 lru['bye'] assert lru.get('hi') is None del lru['bye'] assert 'bye' not in lru assert len(lru) == 0 assert not lru
class CacheModule: def __init__(self, max_size: int, ttl_millis: int, from_remote): ''' :param max_size: max_size of the cache,using LRU :param ttl_millis: max live time for the objects :param from_remote: function to load data if the key not exists ''' self.cache = LRU(max_size=max_size) self.ttl = ttl_millis self.from_remote = from_remote self._lock = RLock() def multi_get(self, keys) -> dict: result, missing = {}, [] now = int(round(time.time() * 1000)) for key in keys: value: dict = self.cache.get(key) # key未命中 if not value: missing.append(key) # key超时 elif now < value['max_time']: missing.append(key) # key存在且不是默认值 elif value['data']: result[key] = value['data'] if len(missing) > 0: loads_d: dict = self.from_remote(missing) dft = { 'max_time': int(round(time.time() * 1000)) + self.ttl, 'data': {} } for key in missing: loads_d.setdefault(key, dft) self.multi_set(loads_d) return result def multi_set(self, data: dict): with self._lock: for k, v in data.items(): self.cache.setdefault(k, v) def remove(self, keys): with self._lock: for key in keys: self.cache.pop(key)
def test_lru_basic(): lru = LRU(max_size=1) repr(lru) # sanity lru['hi'] = 0 lru['bye'] = 1 assert len(lru) == 1 lru['bye'] assert lru.get('hi') is None del lru['bye'] assert 'bye' not in lru assert len(lru) == 0 assert not lru try: lru.pop('bye') except KeyError: pass else: assert False default = object() assert lru.pop('bye', default) is default try: lru.popitem() except KeyError: pass else: assert False lru['another'] = 1 assert lru.popitem() == ('another', 1) lru['yet_another'] = 2 assert lru.pop('yet_another') == 2 lru['yet_another'] = 3 assert lru.pop('yet_another', default) == 3 lru['yet_another'] = 4 lru.clear() assert not lru lru['yet_another'] = 5 second_lru = LRU(max_size=1) assert lru.copy() == lru second_lru['yet_another'] = 5 assert second_lru == lru assert lru == second_lru lru.update(LRU(max_size=2, values=[('a', 1), ('b', 2)])) assert len(lru) == 1 assert 'yet_another' not in lru lru.setdefault('x', 2) assert dict(lru) == {'x': 2} lru.setdefault('x', 3) assert dict(lru) == {'x': 2} assert lru != second_lru assert second_lru != lru