将带过期时间的对象添加到栈中,并在过期时收到通知

3 投票
3 回答
1032 浏览
提问于 2025-04-16 15:07

我需要在我的应用程序中保留一个对象(JSON格式),通常是180秒。之后,客户端可能会回来请求这个对象,可能会,也可能不会。

比较棘手的是,我需要在这个对象过期时得到通知,这样我才能把它放回可用的池子里,如果客户端还没有请求的话。

显而易见的解决办法是使用数据库中的时间戳,然后定期运行一个脚本来检查过期的项目,但这感觉不是最好的解决方案。

理想情况下,我希望能找到类似memcache的东西,当一个项目过期时可以触发一个事件,肯定有这样的产品吧?

我现在的框架是基于python、cherrypy、mongo和memcache,但我乐意进行扩展。

3 个回答

-1

我几乎可以肯定,使用Redis的expire命令可以解决这个问题。这个命令用起来很简单。下面是一个使用Redis命令行工具的例子。

redis> HMSET myhash field1 "Hello" field2 "World"
OK
redis> HGET myhash field1
"Hello"
redis> EXPIRE myhash 180
(integer) 1
redis> HGET myhash field2
"World"
redis> HGET myhash field2 # after 180 secs
(nil)

另外,Python有很多Redis客户端的选择。比如redis-natives-py这个库提供了一个注解@temporary(after=None, at=None)。

关于通知的部分

Redis不会主动通知你。也许你的应用程序有逻辑来假设在N秒后过期,当然你可以随时用不太复杂的EXISTS命令来确认一下。另一种选择是使用greenlets来监控时间。它的做法是创建一个Redis对象,等待N秒,然后再确认一下Redis的状态并发送通知。我之所以推荐greenlet,是因为它比Python线程要轻量得多。

1

如果没有其他程序会使用这组对象,那么在你需要分配一个新对象之前,把过期的对象放回池子里似乎没有必要。如果你在分配新对象之前检查一下过期的对象并把它们放回池子,这样就能很好地清理了。

为了简化清理的过程,可以考虑创建一个包含(过期时间,对象)元组的列表,并使用bisect.insort()来保持这个列表按照过期时间的顺序排列:

que_tuple = (time.time() + EXPIRY_DELAY, obj)
bisect.insort(my_list, que_tuple)

每次你想要保留一个新对象时,可以检查一下有没有过期的对象可以放回池子。通过使用这个有序的列表,你只需要检查第一个元素是否过期,然后按顺序清理,直到找到一个没有过期的对象。大概是这样的:

time_to_act, obj = my_list[0]
while time.time() > time_to_act:
    return_to_pool( obj )   # whatever is necessary to clean-up your objects
    my_list.pop(0)
    time_to_act, obj = my_list[0]

如果你决定需要实现一个计时器,这个队列的概念仍然会很有用。否则,你可能会为每个对象的过期设置一个计时器。通常,系统可用的计时器数量是有限的,你可能会发现资源不够用。过期队列(my_list)允许你只用一个计时器来通知下一个过期的对象,并且这个计时器会重置到列表的新头部。

希望这些对你有帮助!

1

我可能会用 threading.Timer 来实现这个功能。一个 Timer 对象会在一段时间后调用指定的函数,并传入给定的参数。所以你可以写一个函数,让它返回一个 JSON 对象到池子里,然后启动一个定时器,指定这个已经被预留的 JSON 对象。此外,你还可以在定时器触发之前取消它,如果客户端在预留过期之前请求了这个对象,你就需要这么做。

为了管理这个池子,我可能会用一个 dict,其中 JSON 对象是键,如果对象没有被借出,值就是 None;如果被借出,值就是 Timer 实例。你还可以用一个单独的 list 来跟踪下一个应该发放的对象;取出对象时用 pop() 从列表末尾取出,归还时用 append() 把它放回去。要小心在更新这两个结构时可能出现的竞争条件哦!

撰写回答