1、session在redis里面的存储结构2

在上一章讲spring session存储到redis的时候,在redis里面看到每一个session存储都会生成三条记录,记录格式如下:




redis永久保存过期时间如何配置 redis保存session,过期就删除_redis永久保存过期时间如何配置


这就很麻烦了,为啥不能一一对应,做彼此的天使呢,搞的一对三,很影响风化啊。到底是道德的沦丧还是人性的扭曲,让我们走进redis,看下具体的数据内容:
第一个k-v存储这个Session的id,是一个Set类型的Redis数据结构。这个key中的最后的1570550340000值是一个时间戳,根据这个Session过期时刻滚动至下一分钟而计算得出。里面的值是这样的


redis永久保存过期时间如何配置 redis保存session,过期就删除_session删除里面的一条数据_02


第二个k-v用来表示Session在Redis中的过期,是个String类型,这个k-v不存储任何有用数据,只是表示Session过期而设置。这个key在Redis中的过期时间即为Session的过期时间间隔


redis永久保存过期时间如何配置 redis保存session,过期就删除_redis永久保存过期时间如何配置_03


第三个k-v用来存储Session的详细信息,是hash类型,包括Session的创建时间、过期时间间隔、最近的访问时间、attributes等等。这个k的过期时间为Session的最大过期时间 + 5分钟。如果默认的最大过期时间为30分钟,则这个k的过期时间为35分钟


redis永久保存过期时间如何配置 redis保存session,过期就删除_session删除里面的一条数据_04


说到这里,会有个灵魂的拷问,就一个session的存储,为啥要搞三条记录?这里要提一下HttpSession的接口规范了。
session虽然也是一条数据,但是和普通的数据还是有不同的,我们去查httpsession的javadoc可以看到,session的添加和移除都是可以触发事件的。


redis永久保存过期时间如何配置 redis保存session,过期就删除_session删除里面的一条数据_05


触发的前提是对象实现了HttpSessionBindingListener接口,然后我们再看下HttpSessionBindingListener的部分


redis永久保存过期时间如何配置 redis保存session,过期就删除_session删除里面的一条数据_06


里面通过HttpSessionBindingEvent来通知session的attribute变化这一套下来是不是很熟悉,这就是一个典型的监听器模型,也是消息机制的主要表现方式,在spring session通过redis实现分布式的session时,就是通过redis的消息机制来实现标准的session变动通知的,在了解具体的做法先,让我们先看下redis的消息通知功能。

2、啥!redis支持消息的发布订阅?

Redis从2.8.0版本后,推出 Keyspace Notifications 特性。Keyspace Notifications 允许客户端可以以 订阅/发布(Sub/Pub)模式,接收那些对数据库中的键和值有影响的操作事件。这些操作事件具体来说,就是 del , expire , set , rpop等啦。
redis默认不会开启keyspace notifications,因为开启后会对cpu有消耗,所以开启需要额外配置redis.conf文件


############################# EVENT NOTIFICATION ##############################

# Redis can notify Pub/Sub clients about events happening in the key space.
# This feature is documented at http://redis.io/topics/notifications
#
# For instance if keyspace events notification is enabled, and a client
# performs a DEL operation on key "foo" stored in the Database 0, two
# messages will be published via Pub/Sub:
#
# PUBLISH __keyspace@0__:foo del
# PUBLISH __keyevent@0__:del foo
#
# It is possible to select the events that Redis will notify among a set
# of classes. Every class is identified by a single character:
#
#  K     Keyspace events, published with __keyspace@<db>__ prefix.
#  E     Keyevent events, published with __keyevent@<db>__ prefix.
#  g     Generic commands (non-type specific) like DEL, EXPIRE, RENAME, ...
#  $     String commands
#  l     List commands
#  s     Set commands
#  h     Hash commands
#  z     Sorted set commands
#  x     Expired events (events generated every time a key expires)
#  e     Evicted events (events generated when a key is evicted for maxmemory)
#  A     Alias for g$lshzxe, so that the "AKE" string means all the events.
#
#  The "notify-keyspace-events" takes as argument a string that is composed
#  of zero or multiple characters. The empty string means that notifications
#  are disabled.
#
#  Example: to enable list and generic events, from the point of view of the
#           event name, use:
#
#  notify-keyspace-events Elg
#
#  Example 2: to get the stream of the expired keys subscribing to channel
#             name __keyevent@0__:expired use:
#
#  notify-keyspace-events Ex
#
#  By default all notifications are disabled because most users don't need
#  this feature and the feature has some overhead. Note that if you don't
#  specify at least one of K or E, no events will be delivered.
notify-keyspace-events "Ex"


关键就是最下一句,Ex的意思可以看上面的注释。启动后只要我们订阅一个特定的channel,该channel下面的数据变化我们就能收到通知了。
改好配置,重启redis,就能够开启消息通知了。
Redis中带有过期的key有两种方式:

  • 当访问时发现其过期
  • Redis后台逐步查找过期键

问题时访问时发现过期可能会在很久以后,所以怎么才能在key过期的时候就知道呢?
spring-session中有个定时任务,每个整分钟都会查询相应的spring:session:expirations:整分钟的时间戳中的过期SessionId,然后再访问一次这个sessionId,即spring:session:sessions:expires:SessionId,以便能够让Redis及时的产生key过期事件——即Session过期事件。而这个定时任务的时间时可以配置的,配置的参数就时上一章结尾说的cleanupCron


public class RedisHttpSessionConfiguration extends SpringHttpSessionConfiguration
		implements BeanClassLoaderAware, EmbeddedValueResolverAware, ImportAware,
		SchedulingConfigurer {

	static final String DEFAULT_CLEANUP_CRON = "0 * * * * *";

	private String cleanupCron = DEFAULT_CLEANUP_CRON;


默认就是一分钟清理一次过期的缓存,可以根据自己的需求做更改。

3、总结

  1. spring session存到redis的每个session数据会有三条记录,一条存储session自身的信息,另外两条存储session过期的信息
  2. redis可以开启Keyspace Notifications服务,开启后redis的数据出现变动会有通知消息
  3. spring session可以配置定时任务扫描存到redis的session数据中关于过期信息的两条,定时任务的配置在cleanupCron里面

我这里其实讲的不是很详细,关于spring session方面的细节,大家可以看这一系列的文章,讲的很好,但看的挺累。