我想创建一个表示一组队列的数据结构(理想情况下是散列,映射或类似查找的字典),其中队列中的消息在达到特定年龄后被主动删除。 ttl值是全局的;消息不需要也没有个别的ttl。 ttl的分辨率不需要非常准确 - 仅在一秒左右。
我甚至不确定在这里搜索什么。我可以创建一个单独的全局队列,后台线程正在监视,查看和拉出指向全局队列中的消息的指针,告诉它从各个队列中删除项目,但行为需要双向进行。如果某个项目从一个invidual队列中删除,则需要从全局队列中删除。
我希望这个数据结构能够在Python中实现,理想情况下,速度是最重要的(比内存使用更多)。有关从哪里开始的任何建议?
答案 0 :(得分:2)
我首先只是在单个类中建模您正在寻找的行为,尽可能简单地表达。性能可以通过迭代优化来实现,但仅在必要时(您可能不需要它)。
下面的课程大致类似于你所描述的内容。队列只是命名并存储在字典中的列表。每条消息都带有时间戳,并插入列表的前面(FIFO)。通过检查列表末尾的消息的时间戳,然后弹出消息直到它达到低于年龄阈值的消息来获取消息。
如果您计划从多个线程访问它,您需要添加一些细粒度锁定以挤出最大的性能。例如,reap()
方法一次只能锁定1个队列,而不是锁定所有队列(方法级同步),因此您还需要为每个命名队列保持锁定。
已更新 - 现在使用一组全局存储桶(按时间戳,1秒分辨率)来跟踪哪些队列从那时起有消息。这减少了每次通过时要检查的队列数。
import time
from collections import defaultdict
class QueueMap(object):
def __init__(self):
self._expire = defaultdict(lambda *n: defaultdict(int))
self._store = defaultdict(list)
self._oldest_key = int(time.time())
def get_queue(self, name):
return self._store.get(name, [])
def pop(self, name):
queue = self.get_queue(name)
if queue:
key, msg = queue.pop()
self._expire[key][name] -= 1
return msg
return None
def set(self, name, message):
key = int(time.time())
# increment count of messages in this bucket/queue
self._expire[key][name] += 1
self._store[name].insert(0, (key, message))
def reap(self, age):
now = time.time()
threshold = int(now - age)
oldest = self._oldest_key
# iterate over buckets we need to check
for key in range(oldest, threshold + 1):
# for each queue with items, expire the oldest ones
for name, count in self._expire[key].iteritems():
if count <= 0:
continue
queue = self.get_queue(name)
while queue:
if queue[-1][0] > threshold:
break
queue.pop()
del self._expire[key]
# set oldest_key for next pass
self._oldest_key = threshold
用法:
qm = QueueMap()
qm.set('one', 'message 1')
qm.set('one', 'message 2')
qm.set('two', 'message 3')
print qm.pop('one')
print qm.get_queue('one')
print qm.get_queue('two')
# call this on a background thread which sleeps
time.sleep(2)
# reap messages older than 1 second
qm.reap(1)
# queues should be empty now
print qm.get_queue('one')
print qm.get_queue('two')
答案 1 :(得分:0)
每次访问队列时都要考虑检查TTL,而不是使用线程来不断检查。我不确定你对hash / map / dict的意思(关键是什么?),但这样的事情怎么样:
import time
class EmptyException(Exception): pass
class TTLQueue(object):
TTL = 60 # seconds
def __init__(self):
self._queue = []
def push(self, msg):
self._queue.append((time.time()+self.TTL, msg))
def pop(self):
self._queue = [(t, msg) for (t, msg) in self._queue if t > time.time()]
if len(self._queue) == 0:
raise EmptyException()
return self._queue.pop(0)[1]
queues = [TTLQueue(), TTLQueue(), TTLQueue()] # this could be a dict or set or
# whatever if I knew what keys
# you expected