如何处理会话过期基于redis?

L.J*_*J.W 13 session store redis

我想基于Redis实现会话存储.我想将会话数据放入Redis.但我不知道如何处理会话过期.我可以循环遍历所有redis密钥(sessionid)并评估最后的访问时间和最大空闲时间,因此我需要将所有密钥加载到客户端,并且可能有1000m会话密钥并且可能导致非常差的I/O表演.
我想让Redis管理过期,但是当密钥到期时没有监听器或回调,所以不可能触发HttpSessionListener.有什么建议?

Did*_*zia 36

因此,当会话在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到期时间不准确.如果所有密钥都已到期,但只有一个密钥即将过期,并且未访问它,则活动过期作业可能需要几分钟才能找到密钥并使其过期.如果您需要一些准确的通知,这不是要走的路.

解决方案2:使用zsets模拟到期

这里的想法是不依赖于Redis密钥到期机制,而是通过使用附加索引和轮询守护进程来模拟它.它可以使用未经修改的Redis 2.6版本.

每次将会话添加到Redis时,您都可以运行:

MULTI
SET <session id> <session content>
ZADD to_be_expired <current timestamp + session timeout> <session id>
EXEC
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

启动脚本的命令是:

EVAL <script> 1 to_be_expired <current timestamp>
Run Code Online (Sandbox Code Playgroud)

守护进程最多可获得10个项目.对于它们中的每一个,它必须使用DEL命令来删除会话,并通知应用程序.如果实际处理了一个项目(即Lua脚本的返回不为空),则守护程序应立即循环,否则可引入1秒等待状态.

由于Lua脚本,可以并行启动多个轮询守护进程(该脚本保证给定的会话只会被处理一次,因为密钥是由Lua脚本本身从to_be_expired中删除的).

解决方案3:使用外部分布式计时器

另一种解决方案是依赖外部分布式计时器.在豆茎轻量级排队系统是一个很好的可能性

每次在系统中添加会话时,应用程序都会将会话ID发布到beanstalk队列,其延迟对应于会话超时.守护程序正在侦听队列.当它可以使项目出列时,表示会话已过期.它只需要在Redis中清理会话,并通知应用程序.

  • 这已经在redis中实现了.这些问题已经结束.如果有人更新这个答案会很好. (2认同)