我想实现一个基于Redis的会话存储。我想将会话数据放入Redis。但是我不知道如何处理会话过期。我可以遍历所有redis密钥(sessionid)并评估上次访问时间和最大空闲时间,因此我需要将所有密钥加载到客户端中,并且可能有1000m会话密钥,并且可能导致非常差的I / O表演。 我想让Redis管理过期,但是密钥过期时没有侦听器或回调,因此无法触发HttpSessionListener。有什么建议吗?
因此,当会话在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上循环以从“
重要的一点是了解到期机制在Redis中如何工作。实际上,有两种不同的到期路径,它们都同时处于活动状态:
惰性(被动)机制。过期可能会在每次访问密钥时发生。
主动机制。内部作业会定期(随机)对具有到期设置的多个密钥进行采样,以尝试找到要到期的密钥。
请注意,上述补丁在两个路径下均能正常工作。
结果是Redis的到期时间不准确。如果所有密钥都已到期,但是只有一个即将到期,并且无法访问,则活动的到期作业可能需要几分钟才能找到该密钥并使它到期。如果您需要通知中的某些准确性,则不是要走的路。
解决方案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:使用外部分布式计时器
另一种解决方案是依靠外部分布式计时器。该轻量级排队系统魔豆是一个很好的可能性
每次在系统中添加会话时,应用程序都会以与会话超时相对应的延迟将会话ID发布到beantalk队列中。守护程序正在监听队列。当它可以使项目出队时,表示会话已过期。它只需要清理Redis中的会话,然后通知应用程序。