如何处理基于redis的会话过期? [英] how to handle session expire basing redis?

查看:94
本文介绍了如何处理基于redis的会话过期?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I want to implement a session store based on Redis. I would like to put session data into Redis. But I don't know how to handle session-expire. I can loop through all the redis keys (sessionid) and evaluate the last access time and max idle time, thus I need to load all the keys into the client, and there may be 1000m session keys and may lead to very poor I/O performances.
I want to let Redis manage the expire, but there are no listener or callback when the key expire, so it is impossible to trigger HttpSessionListener. Any advice?

推荐答案

因此,您需要在 Redis 中的会话过期时通知您的应用程序.

So you need your application to be notified when a session expires in Redis.

虽然 Redis 不支持此功能,但您可以使用许多技巧来实现它.

While Redis does not support this feature, there are a number of tricks you can use to implement it.

更新:从 2.8.0 版开始,Redis 确实支持此http://redis.io/topics/notifications

首先,人们正在考虑:这仍在讨论中,但可能会添加到 Redis 的未来版本中.请参阅以下问题:

First, people are thinking about it: this is still under discussion, but it might be added to a future version of Redis. See the following issues:

现在,这里有一些可以用于当前 Redis 版本的解决方案.

Now, here are some solutions you can use with the current Redis versions.

方案一:给Redis打补丁

其实,在Redis执行key过期时添加一个简单的通知并没有那么难.在Redis源码的db.c文件中增加10行即可实现.下面是一个例子:

Actually, adding a simple notification when Redis performs key expiration is not that hard. It can be implemented by adding 10 lines to the db.c file of Redis source code. Here is an example:

https://gist.github.com/3258233

如果密钥已过期并以@"字符开头(任意选择),则此短补丁会将密钥发布到 #expired 列表.它可以轻松适应您的需求.

This short patch posts a key to the #expired list if the key has expired and starts with a '@' character (arbitrary choice). It can easily be adapted to your needs.

然后使用 EXPIRE 或 SETEX 命令来设置会话对象的过期时间,并编写一个小守护进程在 BRPOP 上循环以从#expired"列表中出列,并在您的应用程序中传播通知.

It is then trivial to use the EXPIRE or SETEX commands to set an expiration time for your session objects, and write a small daemon which loops on BRPOP to dequeue from the "#expired" list, and propagate the notification in your application.

重要的一点是要了解Redis中的过期机制是如何工作的.实际上有两种不同的到期路径,都同时处于活动状态:

An important point is to understand how the expiration mechanism works in Redis. There are actually two different paths for expiration, both active at the same time:

  • 懒惰(被动)机制.每次访问密钥时都可能发生过期.

  • Lazy (passive) mechanism. The expiration may occur each time a key is accessed.

主动机制.内部作业定期(随机)采样一些设置了过期时间的键,试图找到要过期的键.

Active mechanism. An internal job regularly (randomly) samples a number of keys with expiration set, trying to find the ones to expire.

请注意,上述补丁适用于两个路径.

Note that the above patch works fine with both paths.

后果是Redis过期时间不准确.如果所有的key都过期了,但是只有一个即将过期,并且没有被访问,那么active的过期作业可能需要几分钟的时间才能找到key并过期.如果您需要通知的准确性,这不是可行的方法.

The consequence is Redis expiration time is not accurate. If all the keys have expiration, but only one is about to be expired, and it is not accessed, the active expiration job may take several minutes to find the key and expired it. If you need some accuracy in the notification, this is not the way to go.

方案二:用zsets模拟过期

这里的想法是不依赖Redis key过期机制,而是通过使用一个额外的索引加上一个轮询守护进程来模拟它.它可以与未修改的 Redis 2.6 版本一起使用.

The idea here is to not rely on the Redis key expiration mechanism, but simulate it by using an additional index plus a polling daemon. It can work with an unmodified Redis 2.6 version.

每次添加一个会话到Redis,就可以运行:

Each time a session is added to Redis, you can run:

MULTI
SET <session id> <session content>
ZADD to_be_expired <current timestamp + session timeout> <session id>
EXEC

to_be_expired 排序集只是一种访问应该过期的第一个键的有效方法.守护进程可以使用以下 Lua 服务器端脚本轮询 to_be_expired:

The to_be_expired sorted set is just an efficient way to access the first keys that should be expired. A daemon can poll on to_be_expired using the following Lua server-side script:

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 秒等待状态.

The daemon will get at most 10 items. For each of them, it has to use the DEL command to remove the sessions, and notify the application. If one item was actually processed (i.e. the return of the Lua script is not empty), the daemon should loop immediately, otherwise a 1 second wait state can be introduced.

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

Thanks to the Lua script, it is possible to launch several polling daemons in parallel (the script guarantees that a given session will only be processed once, since the keys are removed from to_be_expired by the Lua script itself).

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

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

Another solution is to rely on an external distributed timer. The beanstalk lightweight queuing system is a good possibility for this

每次在系统中添加会话时,应用程序将会话 ID 发布到 beantalk 队列,延迟与会话超时相对应.守护进程正在侦听队列.当它可以使项目出列时,就意味着会话已过期.它只需要清理 Redis 中的会话,并通知应用程序.

Each time a session is added in the system, the application posts the session ID to a beanstalk queue with a delay corresponding to the session time out. A daemon is listening to the queue. When it can dequeue an item, it means a session has expired. It just has to clean the session in Redis, and notify the application.

这篇关于如何处理基于redis的会话过期?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆