我想实现一个基于Redis的会话存储。我想把会话数据放到Redis中。但是我不知道如何处理会话过期。我可以遍历所有redis密钥(sessionid),并评估最后访问时间和最大空闲时间,因此我需要将所有密钥加载到客户端,并且可能有1000m会话密钥,可能会导致非常差的I/O性能。
我想让Redis来管理过期,但是密钥过期时没有监听器,也没有回调,所以不可能触发HttpSessionListener。有什么建议吗?
发布于 2012-08-05 18:12:52
因此,您需要在Redis中的会话到期时通知您的应用程序。
虽然Redis不支持此功能,但您可以使用许多技巧来实现它。
Redis更新:从2.8.0版本开始,不支持此
首先,人们正在考虑它:这仍在讨论中,但它可能会添加到未来版本的Redis中。请参阅以下问题:
现在,这里有一些你可以在当前的Redis版本中使用的解决方案。
解决方案1:修补Redis
实际上,在Redis执行密钥过期时添加一个简单的通知并不难。在Redis源码的db.c文件中添加10行即可实现。下面是一个示例:
https://gist.github.com/3258233
如果密钥已过期并以'@‘字符开头(任意选择),则此简短补丁将密钥发布到#expired列表。它可以很容易地适应您的需求。
然后就可以使用EXPIRE或SETEX命令来设置会话对象的过期时间,并编写一个在BRPOP上循环的小守护进程,以便从"#expired“列表中出队,并在应用程序中传播通知。
重要的一点是理解Redis中的过期机制是如何工作的。实际上有两种不同的过期路径,这两种路径都同时处于活动状态:
请注意,上面的补丁在两个路径上都可以很好地工作。
结果是Redis的过期时间不准确。如果所有密钥都已过期,但只有一个密钥即将过期,并且未被访问,则活动的过期作业可能需要几分钟才能找到密钥并使其过期。如果你需要在通知中保持一定的准确性,这不是你应该做的。
解决方案2:使用zsets模拟过期
这里的想法是不依赖于Redis密钥过期机制,而是通过使用额外的索引加上轮询守护进程来模拟它。它可以与未修改的Redis 2.6版本一起工作。
每次在Redis中添加一个会话,您都可以运行:
MULTI
SET <session id> <session content>
ZADD to_be_expired <current timestamp + session timeout> <session id>
EXECto_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中的会话,并通知应用程序。
https://stackoverflow.com/questions/11810020
复制相似问题