#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
#
# Copyright 2018 黎慧剑
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
"""
缓存处理框架
@module cache
@file cache.py
"""
import threading
import functools
import time
from enum import Enum
from abc import ABC, abstractmethod # 利用abc模块实现抽象类
__MOUDLE__ = 'cache' # 模块名
__DESCRIPT__ = u'缓存处理框架' # 模块描述
__VERSION__ = '0.1.0' # 版本
__AUTHOR__ = u'黎慧剑' # 作者
__PUBLISH__ = '2018.09.01' # 发布日期
[文档]class EnumCacheSortedOrder(Enum):
"""
缓存排序优先规则
@enum {string}
"""
HitTimeFirst = 'HitTimeFirst' # 按命中时间优先排序
HitCountFirst = 'HitCountFirst' # 按命中次数优先排序
[文档]class BaseCache(ABC):
"""
基础缓存理定义基类, 定义缓存处理的基本框架函数
@param {int} size=10 - 缓存大小,<=0 代表没有限制
@param {EnumCacheSortedOrder} sorted_order=EnumCacheSortedOrder.HitTimeFirst - 缓存排序优先规则
"""
#############################
# 内部变量
#############################
_cache_size = 10 # 缓存大小,<=0 代表没有限制
# 缓存使用情况登记字典
# key为缓存唯一识别标识,value为使用情况登记字典,固定登记信息key包括:
# last_hit_time - datetime最后一次命中时间,hit_count - int 命中次数
_cache_hit_info = None
_cache_data = None # 缓存数据登记字典,key为缓存唯一识别标识,value为缓存数据
_sortedorder = EnumCacheSortedOrder.HitTimeFirst # 缓存排序优先规则
_cache_change_lock = None # 为保证缓存信息的一致性,需要控制的锁
#############################
# 构造函数
#############################
[文档] def __init__(self, size=10, sorted_order=EnumCacheSortedOrder.HitTimeFirst):
"""
构造函数
@param {int} size=10 - 缓存大小,<=0 代表没有限制
@param {EnumCacheSortedOrder} sorted_order=EnumCacheSortedOrder.HitTimeFirst - 缓存排序优先规则
"""
self._cache_size = size
self._sortedorder = sorted_order
self._cache_hit_info = dict()
self._cache_data = dict()
self._cache_change_lock = threading.RLock()
#############################
# 内部函数
#############################
@staticmethod
def _hit_info_sorted_compare(hit_info_x, hit_info_y, sorted_order=EnumCacheSortedOrder.HitTimeFirst):
"""
用于进行命中信息的排序处理(字典的sorted排序函数)
@param {dict} hit_info_x - 命中信息字典x,格式为[{'last_hit_time':?, 'hit_count':?}, key]
@param {dict} hit_info_y - 命中信息字典y,格式为[{'last_hit_time':?, 'hit_count':?}, key]
@param {EnumCacheSortedOrder} sorted_order=EnumCacheSortedOrder.HitTimeFirst - 缓存排序优先规则
@returns {int} - 比较结果:
0 : 相等
-1 : x 排在前面
1 : y 排在前面
"""
if sorted_order == EnumCacheSortedOrder.HitTimeFirst:
# 命中时间优先
if hit_info_x[0]['last_hit_time'] > hit_info_y[0]['last_hit_time']:
return -1
elif hit_info_x[0]['last_hit_time'] < hit_info_y[0]['last_hit_time']:
return 1
else:
if hit_info_x[0]['hit_count'] > hit_info_y[0]['hit_count']:
return -1
elif hit_info_x[0]['hit_count'] < hit_info_y[0]['hit_count']:
return 1
else:
return 0
else:
# 命中次数优先
if hit_info_x[0]['hit_count'] > hit_info_y[0]['hit_count']:
return -1
elif hit_info_x[0]['hit_count'] < hit_info_y[0]['hit_count']:
return 1
else:
if hit_info_x[0]['last_hit_time'] > hit_info_y[0]['last_hit_time']:
return -1
elif hit_info_x[0]['last_hit_time'] < hit_info_y[0]['last_hit_time']:
return 1
else:
return 0
@staticmethod
def _hit_info_sorted_compare_by_hit_time(hit_info_x, hit_info_y):
return BaseCache._hit_info_sorted_compare(hit_info_x, hit_info_y, EnumCacheSortedOrder.HitTimeFirst)
@staticmethod
def _hit_info_sorted_compare_by_hit_count(hit_info_x, hit_info_y):
return BaseCache._hit_info_sorted_compare(hit_info_x, hit_info_y, EnumCacheSortedOrder.HitCountFirst)
def _get_keys_sorted(self):
"""
获取排好序的key列表
@returns {list} - 排好序的缓存唯一标识列表
"""
_sort_list = [(v, k) for k, v in self._cache_hit_info.items()]
# 排序
if self._sortedorder == EnumCacheSortedOrder.HitTimeFirst:
_sort_list.sort(key=functools.cmp_to_key(
BaseCache._hit_info_sorted_compare_by_hit_time))
else:
_sort_list.sort(key=functools.cmp_to_key(
BaseCache._hit_info_sorted_compare_by_hit_count))
# 返回key列表
return [k[1] for k in _sort_list]
def _check_size_and_cut(self):
"""
检查缓存列表是否超过指定大小,如果超过则按优先级从后删除缓存
"""
if self._cache_size <= 0:
return
_key_list = self._get_keys_sorted()
_len = len(_key_list)
# print(_key_list)
if self._sortedorder == EnumCacheSortedOrder.HitCountFirst:
# 按点击数处理的,要考虑新加进来的项点击数为0,只保留一个点击数为0的项
_del_count = _len - self._cache_size
while _del_count > 0:
if self._cache_hit_info[_key_list[_len - 1]]['hit_count'] != 0:
# 当前节点点击数不为0,直接删除
self.del_cache(_key_list[_len - 1])
_del_count -= 1
else:
# 当前节点点击数为0,如果上一个节点的点击数为0,则删除自己
if self._cache_hit_info[_key_list[_len - 2]]['hit_count'] == 0:
self.del_cache(_key_list[_len - 1])
_del_count -= 1
# 下一个循环
_len -= 1
else:
# 按点击时间处理的,直接取消最后一个就好
while _len - self._cache_size > 0:
self.del_cache(_key_list[_len - 1])
_len -= 1
#############################
# 公共处理函数
#############################
[文档] def clear(self):
"""
清除所有缓存
"""
# 先清除数据,再清除其他
self._clear_cache_data()
self._cache_change_lock.acquire()
try:
self._cache_hit_info.clear()
self._cache_data.clear()
finally:
self._cache_change_lock.release()
[文档] def get_cache(self, key):
"""
获取指定key的缓存数据
@param {string} key - 缓存唯一标识
@returns {object} - 具体缓存data,返回None代表没有缓存
"""
_value = None
self._cache_change_lock.acquire()
try:
if key not in self._cache_data.keys():
return None
_value = self._cache_data[key]
finally:
self._cache_change_lock.release()
_data = self._get_cache_data(key=key, value=_value)
self._cache_change_lock.acquire()
try:
if _data is None:
# 说明该数据已经被清理掉了,清理掉内存信息
if key in self._cache_data.keys():
del self._cache_data[key]
if key in self._cache_hit_info.keys():
del self._cache_hit_info[key]
else:
# 更新命中信息
# print(key + ":" + str(time.time()))
if key in self._cache_hit_info.keys():
self._cache_hit_info[key]['last_hit_time'] = time.time()
self._cache_hit_info[key]['hit_count'] = self._cache_hit_info[key]['hit_count'] + 1
else:
self._cache_hit_info[key] = {
'last_hit_time': time.time(),
'hit_count': 0
}
# print(self._cache_hit_info)
finally:
self._cache_change_lock.release()
return _data
[文档] def update_cache(self, key, data):
"""
更新缓存数据
@param {string} key - 缓存唯一标识
@param {object} data - 要更新的缓存数据
"""
_value = None
self._cache_change_lock.acquire()
try:
if key in self._cache_data.keys():
_value = self._cache_data[key]
finally:
self._cache_change_lock.release()
# 先存入缓存数据
_ret_value = self._update_cache_data(key=key, value=_value, data=data)
# 更新数据
self._cache_change_lock.acquire()
try:
self._cache_data[key] = _ret_value
# print(key + ":" + str(time.time()))
if key in self._cache_hit_info.keys():
self._cache_hit_info[key]['last_hit_time'] = time.time()
self._cache_hit_info[key]['hit_count'] = self._cache_hit_info[key]['hit_count'] + 1
else:
self._cache_hit_info[key] = {
'last_hit_time': time.time(),
'hit_count': 0
}
finally:
self._cache_change_lock.release()
# 检查是否超过大小限制
self._check_size_and_cut()
[文档] def del_cache(self, key):
"""
删除指定缓存
@param {string} key - 缓存唯一标识
"""
_value = None
self._cache_change_lock.acquire()
try:
if key in self._cache_data.keys():
_value = self._cache_data[key]
else:
# 不存在缓存
return
finally:
self._cache_change_lock.release()
# 执行数据删除
self._del_cache_data(key=key, value=_value)
# 删除索引
self._cache_change_lock.acquire()
try:
if key in self._cache_data.keys():
del self._cache_data[key]
if key in self._cache_hit_info.keys():
del self._cache_hit_info[key]
finally:
self._cache_change_lock.release()
[文档] def get_cache_keys(self):
"""
返回缓存唯一标识列表
@returns {list} - 已按优先级排好序的key列表
"""
return self._get_keys_sorted()
#############################
# 需继承类实现的内部处理函数
#############################
@abstractmethod
def _clear_cache_data(self):
"""
清除缓存所有实际数据
该函数应实现清除所有缓存数据的操作(注意非_cache_data字典中的value,但可以根据该value遍历)
"""
pass
@abstractmethod
def _get_cache_data(self, key, value):
"""
获取指定缓存数据
@param {string} key - 缓存唯一标识
@param {object} value - _cache_data字典中的value(可能是真实数据的索引)
@returns {object} - 具体缓存data,返回None代表没有缓存
"""
pass
@abstractmethod
def _update_cache_data(self, key, value, data):
"""
更新缓存数据
@param {string} key - 缓存唯一标识
@param {object} value - _cache_data字典中的value(如果原来已有数据)
@param {object} data - 要更新的缓存数据
@returns {object} - 更新完成后需同步到_cache_data字典中value
"""
pass
@abstractmethod
def _del_cache_data(self, key, value):
"""
删除指定缓存数据
@decorators abstractmethod - [description]
@param {string} key - 缓存唯一标识
@param {object} value - _cache_data字典中的value
"""
pass
[文档]class MemoryCache(BaseCache):
"""
内存缓存
直接继承原生BaseCache定义的方法,通过_cache_data直接存储数据
"""
#############################
# 需继承类实现的内部处理函数
#############################
def _clear_cache_data(self):
"""
清除缓存所有实际数据, 由于不涉及另行存储数据,无需处理
"""
return
def _get_cache_data(self, key, value):
"""
获取指定缓存数据
@param {string} key - 缓存唯一标识
@param {object} value - _cache_data字典中的value(可能是真实数据的索引)
@returns {object} - 具体缓存data,直接返回传入的value
"""
return value
def _update_cache_data(self, key, value, data):
"""
更新缓存数据
@param {string} key - 缓存唯一标识
@param {object} value - _cache_data字典中的value(如果原来已有数据)
@param {object} data - 要更新的缓存数据
@returns {object} - 更新完成后需同步到_cache_data字典中value,直接返回传入的data
"""
return data
def _del_cache_data(self, key, value):
"""
删除指定缓存数据
@param {string} key - 缓存唯一标识
@param {object} value - _cache_data字典中的value
"""
return
if __name__ == '__main__':
# 当程序自己独立运行时执行的操作
# 打印版本信息
print(('模块名:%s - %s\n'
'作者:%s\n'
'发布日期:%s\n'
'版本:%s' % (__MOUDLE__, __DESCRIPT__, __AUTHOR__, __PUBLISH__, __VERSION__)))