小编典典

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

redis

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


阅读 425

收藏
2020-06-20

共1个答案

小编典典

因此,当会话在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:使用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中的会话,然后通知应用程序。

2020-06-20