Caching 源码分析
Django 的 cache 缓存机制,包含了一些代理设计模式(代理了但没完全代理,多此一举)。
通过实现一个CacheHandler的manager类,来实现多缓存后端的统一管理和调用,避免到处实例使用。
缓存的目的
缓存的目的就是为了提高系统的性能.
- 存储一些变化少的热点数据,减少对数据库的访问次数
- 存储临时数据, 降低数据库的压力
- 存储计算结果, 降低计算的压力
缓存框架要考虑的方面
- 缓存的淘汰策略, 超过容量 LRU, FIFO, 过期时间
- 缓存的存储策略, 如内存缓存, 文件缓存, 数据库缓存
- 缓存key的管理
代理模式
通过一个外部 Proxy 来访问真实 cache 对象的属性和方法。
这个ConnectionProxy
可以学习他用到的魔法方法,但本质上和设计模式没太多关系。
整个django项目里一共出现两次,一次在cache中作为default cache
的入口,一次在db中作为defult db
的入口
python
# 没啥用,直接用caches['default']代替即可
class ConnectionProxy:
"""Proxy for accessing a connection object's attributes."""
def __init__(self, connections, alias):
self.__dict__["_connections"] = connections
self.__dict__["_alias"] = alias
# 重写__getattr__方法, 使得ConnectionProxy可以像访问真实的connection对象一样访问属性和方法
def __getattr__(self, item):
return getattr(self._connections[self._alias], item)
# 重写__setattr__方法, 使得ConnectionProxy可以像访问真实的connection对象一样设置属性和方法
def __setattr__(self, name, value):
return setattr(self._connections[self._alias], name, value)
# 重写__delattr__方法, 使得ConnectionProxy可以像访问真实的connection对象一样删除属性和方法
def __delattr__(self, name):
return delattr(self._connections[self._alias], name)
# 重写__contains__方法, 使得ConnectionProxy可以使用 `key in ConnectionProxy`的语法来判断key是否存在于缓存中, 实际实现在BaseCache的各个子类中实现
def __contains__(self, key):
return key in self._connections[self._alias]
# 重写__eq__方法, 使得ConnectionProxy可以使用 `ConnectionProxy == other`的语法来判断两个ConnectionProxy是否指向同一个缓存对象, 实际实现在BaseCache的各个子类中实现
# 其实可以用total_ordering装饰器来实现__eq__方法, 但是为了保持一致性, 这里还是自己实现
def __eq__(self, other):
return self._connections[self._alias] == other
缓存基础类
可以学习的地方
- 参数默认值, 通常避免使用一些可变容器对象(list, dict),因为如果代码不严谨,容易出错。
但是编辑器提示的时候,会告诉你默认值是 None,失去了一定的可读性。
所以可以参照 Django 的做法,使用一个名字对象来代替默认值参数。
python
# 通常做法
def get_backend_timeout(self, timeout=None):
"""
Return the timeout value usable by this backend based upon the provided
"""
if timeout is None:
timeout = self.default_timeout
return timeout
# 改进做法
DEFAULT_TIMEOUT = object() # python模块单例
def get_backend_timeout(self, timeout=DEFAULT_TIMEOUT):
"""
Return the timeout value usable by this backend based upon the provided
"""
if timeout is DEFAULT_TIMEOUT: # is 比较内存地址
timeout = self.default_timeout
return timeout
- contains方法
实现 contains 方法可以改变in
操作的结果
python
def __contains__(self, key):
"""
Return True if the key is in the cache and has not expired.
"""
# This is a separate method, rather than just a copy of has_key(),
# so that it always has the same functionality as has_key(), even
# if a subclass overrides it.
return self.has_key(key)
总结
其他部分就是 BaseCache 的子类了,用对应的 client 实现缓存的方法。