因此,您需要在 Redis 中的会话到期时通知您的应用程序。
虽然 Redis 不支持此功能,但您可以使用许多技巧来实现它。
更新:从 2.8.0 版本开始,Redis 支持 http://redis.io/topics/notifications
首先,人们正在考虑它:这仍在讨论中,但它可能会添加到 Redis 的未来版本中。请参阅以下问题:
现在,这里有一些您可以在当前 Redis 版本中使用的解决方案。
解决方案 1:修补 Redis
实际上,在 Redis 执行密钥过期时添加一个简单的通知并不难。可以通过在Redis源码的db.c文件中增加10行来实现。这是一个例子:
https://gist.github.com/3258233
如果密钥已过期并以“@”字符开头(任意选择),则此简短补丁会将密钥发布到 #expired 列表。它可以很容易地适应您的需求。
然后使用 EXPIRE 或 SETEX 命令为您的会话对象设置过期时间,并编写一个小守护程序,该守护程序在 BRPOP 上循环以从“#expired”列表中出列,并在您的应用程序中传播通知.
重要的一点是要了解 Redis 中的过期机制是如何工作的。实际上有两种不同的过期路径,都同时激活:
请注意,上述补丁适用于两种路径。
后果是Redis过期时间不准确。如果所有的key都过期了,但是只有一个即将过期,并且没有被访问,活跃的过期作业可能需要几分钟才能找到key并过期。如果您需要通知的准确性,那么这不是可行的方法。
解决方案 2:使用 zset 模拟到期
这里的想法是不依赖 Redis 密钥过期机制,而是通过使用额外的索引和轮询守护进程来模拟它。它可以与未经修改的 Redis 2.6 版本一起使用。
每次将会话添加到 Redis 时,您可以运行:
MULTI
SET <session id> <session content>
ZADD to_be_expired <current timestamp + session timeout> <session id>
EXEC
to_be_expired 排序集只是访问第一个应该过期的键的有效方法。守护进程可以使用以下 Lua 服务器端脚本轮询 to_be_expired:
local res = redis.call('ZRANGEBYSCORE',KEYS[1], 0, ARGV[1], 'LIMIT', 0, 10 )
if #res > 0 then
redis.call( 'ZREMRANGEBYRANK', KEYS[1], 0, #res-1 )
return res
else
return false
end
启动脚本的命令是:
EVAL <script> 1 to_be_expired <current timestamp>
守护程序最多会获得 10 个项目。对于它们中的每一个,它必须使用 DEL 命令来删除会话,并通知应用程序。如果实际处理了一项(即 Lua 脚本的返回不为空),则守护程序应立即循环,否则可引入 1 秒等待状态。
借助 Lua 脚本,可以并行启动多个轮询守护进程(该脚本保证给定会话只会被处理一次,因为 Lua 脚本本身会从 to_be_expired 中删除密钥)。
解决方案 3:使用外部分布式计时器
另一种解决方案是依赖外部分布式计时器。 beanstalk lightweight queuing system 是一个很好的可能性
每次在系统中添加会话时,应用程序都会将会话 ID 发布到 beanstalk 队列,延迟时间对应于会话超时。一个守护进程正在监听队列。当它可以使一个项目出队时,这意味着一个会话已经过期。它只需要清理 Redis 中的会话,并通知应用程序。