周天又寂寞了….今天天气不是太好,有些阴冷,估计大街上的小妞肯定不会穿裙子… 估计不能拿着板凳去看美女的大腿了….
最近一直对服务端的开发很是感兴趣,python本身的数据结构是很容易实现队列或者是缓存的服务的。 我曾经实现一个python机遇lru算法的一个缓存服务。 别问我Lru缓存算法是啥, 可以理解为保持热数据和新数据的缓存的策略,算法会剔除不活跃的数据。
正好这两天在家看redis的设计与实现,想到了redis的expire的功能, 他的expire是分两种,一个是间隔性质的去剔除过期的数据,还有一种是被动的剔除。 如果python的lru的数据源是在redis里面的话,很容易实现含有expire的lru。 lru和expire在缓存的策略是可以单独存在,又可以互相依赖的。 lru是用来保证存储的数据是活跃的,热的。 含有expire的缓存,是保证数据是新的,就算你的数据再热,但是他时间长了,就应该剔除他。 那么lru和expire如果一起用的话,不仅能实现热数据的存在,而且能实现数据过期。
python实现expire max的原文链接
这次就不用redis做数据源了,而是采用python的orderDict来实现。 orderDict是可以让字典有序的模块,orderDict自己会维持一个列表,通过这个列表来实现kv的有序。 我们主要是为了实现队列和缓存,首先先用orderDict实现一个简单的、类似FIFO先进先出的队列服务,附带了最大数据的控制。 我们会发现利用orderDict实现队列也是很容易的。
from collections import OrderedDict
class LocalCache(OrderedDict):
"""会删除旧的数据,popitem默认是从最后去除的,我们可以指定从头部去掉 """
def __init__(self, limit=None):
super(LocalCache, self).__init__()
self.limit = limit
def __setitem__(self, key, value):
while len(self) >= self.limit:
self.popitem(last=False)
super(LocalCache, self).__setitem__(key, value)
那咱们再度深入下,基于上面的例子,我们该如何实现expire这个含有TTL功能,应该是如何的实现呢.. 有人已经在githun里面分享了代码.
https://github.com/mailgun/expiringdict/blob/master/expiringdict/__init__.py
import time
from threading import RLock
#Rlock 是用来保持数据,唯一锁
try:
from collections import OrderedDict
except ImportError:
# Python < 2.7
from ordereddict import OrderedDict
class ExpiringDict(OrderedDict):
def __init__(self, max_len, max_age_seconds):
assert max_age_seconds >= 0
assert max_len >= 1
OrderedDict.__init__(self)
self.max_len = max_len
self.max_age = max_age_seconds
self.lock = RLock()
#这里用的是python魔法方法,__contains__ ,每次get数据的时候,都会看看他是否过期,如果过期的话,直接干掉. 逻辑是被with lock包含的,做了一个互斥的效果。
def __contains__(self, key):
""" Return True if the dict has a key, else return False. """
try:
with self.lock:
item = OrderedDict.__getitem__(self, key)
if time.time() - item[1] < self.max_age:
return True
else:
del self[key]
except KeyError:
pass
return False
# 和上面contains差不多的功能
def __getitem__(self, key, with_age=False):
""" Return the item of the dict.
Raises a KeyError if key is not in the map.
"""
with self.lock:
item = OrderedDict.__getitem__(self, key)
item_age = time.time() - item[1]
if item_age < self.max_age:
if with_age:
return item[0], item_age
else:
return item[0]
else:
del self[key]
raise KeyError(key)
#查看数据的个数是否已经等同于先前设置的大小,如果匹配max_len的条件,就从头部干掉一个。 这里OrderDict可以set我们传递的key value 并传送时间戳
def __setitem__(self, key, value):
""" Set d[key] to value. """
with self.lock:
if len(self) == self.max_len:
self.popitem(last=False)
OrderedDict.__setitem__(self, key, (value, time.time()))
def pop(self, key, default=None):
""" Get item from the dict and remove it.
Return default if expired or does not exist. Never raise KeyError.
"""
with self.lock:
try:
item = OrderedDict.__getitem__(self, key)
del self[key]
return item[0]
except KeyError:
return default
def ttl(self, key):
""" 查看ttl时间
"""
key_value, key_age = self.get(key, with_age=True)
if key_age:
key_ttl = self.max_age - key_age
if key_ttl > 0:
return key_ttl
return None
def get(self, key, default=None, with_age=False):
" Return the value for key if key is in the dictionary, else default. "
try:
return self.__getitem__(key, with_age)
except KeyError:
if with_age:
return default, None
else:
return default
def items(self):
""" Return a copy of the dictionary's list of (key, value) pairs. """
r = []
for key in self:
try:
r.append((key, self[key]))
except KeyError:
pass
return r
def values(self):
""" Return a copy of the dictionary's list of values.
See the note for dict.items(). """
r = []
for key in self:
try:
r.append(self[key])
except KeyError:
pass
return r
def fromkeys(self):
" Create a new dictionary with keys from seq and values set to value. "
raise NotImplementedError()
def iteritems(self):
""" Return an iterator over the dictionary's (key, value) pairs. """
raise NotImplementedError()
def itervalues(self):
""" Return an iterator over the dictionary's values. """
raise NotImplementedError()
def viewitems(self):
" Return a new view of the dictionary's items ((key, value) pairs). "
raise NotImplementedError()
def viewkeys(self):
""" Return a new view of the dictionary's keys. """
raise NotImplementedError()
def viewvalues(self):
""" Return a new view of the dictionary's values. """
raise NotImplementedError()
这样expire和max的功能都有了,到现在为止,这个模块还只是实现了redis主键消逝的被动模式,如果是想用主动模式的话,是需要在模块里面开一个线程,一直去loop了。 个人觉得没必要设计一套可以主动剔除失效key的逻辑。 一个简单的服务,你还要开一个线程搞探测实在是折腾,如果非要用主动探测的话,我觉得可以set 和 get的时候,取模计算后顺手再去探测。


while len(self) >= self.limit: 第一段代码,没转义
是js代码颜色的问题