public class RedisOperationsSessionRepository extends Object implements SessionRepository<org.springframework.session.data.redis.RedisOperationsSessionRepository.RedisSession>
A SessionRepository that is implemented
using Spring Data's
RedisOperations. In a web
environment, this is typically used in combination with
SessionRepositoryFilter. This implementation supports
SessionDestroyedEvent through SessionMessageListener.
JedisConnectionFactory factory = new JedisConnectionFactory();
RedisOperationsSessionRepository redisSessionRepository = new RedisOperationsSessionRepository(
factory);
For additional information on how to create a RedisTemplate, refer to the Spring Data Redis Reference.
Each session is stored in Redis as a Hash. Each session is set and updated using the HMSET command. An example of how each session is stored can be seen below.
HMSET spring:session:sessions:<session-id> creationTime 1404360000000 maxInactiveInterval 1800 lastAccessedTime 1404360000000 sessionAttr:<attrName> someAttrValue sessionAttr2:<attrName> someAttrValue2
An expiration is associated to each session using the EXPIRE command based upon the
RedisOperationsSessionRepository.RedisSession.getMaxInactiveIntervalInSeconds()
. For example:
EXPIRE spring:session:sessions:<session-id> 1800
The RedisSession keeps track of the properties that have changed and
only updates those. This means if an attribute is written once and read many
times we only need to write that attribute once. For example, assume the
session attribute "sessionAttr2" from earlier was updated. The following
would be executed upon saving:
HMSET spring:session:sessions:<session-id> sessionAttr2:<attrName> newValue
EXPIRE spring:session:sessions:<session-id> 1800
Spring Session relies on the expired and delete keyspace notifications from Redis to fire a <<SessionDestroyedEvent>>. It is the `SessionDestroyedEvent` that ensures resources associated with the Session are cleaned up. For example, when using Spring Session's WebSocket support the Redis expired or delete event is what triggers any WebSocket connections associated with the session to be closed.
One problem with this approach is that Redis makes no guarantee of when the expired event will be fired if they key has not been accessed. Specifically the background task that Redis uses to clean up expired keys is a low priority task and may not trigger the key expiration. For additional details see Timing of expired events section in the Redis documentation.
To circumvent the fact that expired events are not guaranteed to happen we can ensure that each key is accessed when it is expected to expire. This means that if the TTL is expired on the key, Redis will remove the key and fire the expired event when we try to access they key.
For this reason, each session expiration is also tracked to the nearest minute. This allows a background task to access the potentially expired sessions to ensure that Redis expired events are fired in a more deterministic fashion. For example:
SADD spring:session:expirations:<expire-rounded-up-to-nearest-minute> <session-id>
EXPIRE spring:session:expirations:<expire-rounded-up-to-nearest-minute> 1800
The background task will then use these mappings to explicitly request each key. By accessing they key, rather than deleting it, we ensure that Redis deletes the key for us only if the TTL is expired.
NOTE: We do not explicitly delete the keys since in some instances there may be a race condition that incorrectly identifies a key as expired when it is not. Short of using distributed locks (which would kill our performance) there is no way to ensure the consistency of the expiration mapping. By simply accessing the key, we ensure that the key is only removed if the TTL on that key is expired.
| Constructor and Description |
|---|
RedisOperationsSessionRepository(org.springframework.data.redis.connection.RedisConnectionFactory redisConnectionFactory)
Allows creating an instance and uses a default
RedisOperations for both managing the session and the expirations. |
RedisOperationsSessionRepository(org.springframework.data.redis.core.RedisOperations<String,ExpiringSession> sessionRedisOperations)
Creates a new instance.
|
| Modifier and Type | Method and Description |
|---|---|
void |
cleanupExpiredSessions() |
org.springframework.session.data.redis.RedisOperationsSessionRepository.RedisSession |
createSession()
Creates a new
Session that is capable of being persisted by this SessionRepository. |
void |
delete(String sessionId)
|
org.springframework.session.data.redis.RedisOperationsSessionRepository.RedisSession |
getSession(String id)
|
void |
save(org.springframework.session.data.redis.RedisOperationsSessionRepository.RedisSession session)
Ensures the
Session created by SessionRepository.createSession() is saved. |
void |
setDefaultMaxInactiveInterval(int defaultMaxInactiveInterval)
Sets the maximum inactive interval in seconds between requests before newly created sessions will be
invalidated.
|
public RedisOperationsSessionRepository(org.springframework.data.redis.connection.RedisConnectionFactory redisConnectionFactory)
RedisOperations for both managing the session and the expirations.redisConnectionFactory - the RedisConnectionFactory to use.public RedisOperationsSessionRepository(org.springframework.data.redis.core.RedisOperations<String,ExpiringSession> sessionRedisOperations)
sessionRedisOperations - The RedisOperations to use for managing the sessions. Cannot be null.public void setDefaultMaxInactiveInterval(int defaultMaxInactiveInterval)
defaultMaxInactiveInterval - the number of seconds that the Session should be kept alive between
client requests.public void save(org.springframework.session.data.redis.RedisOperationsSessionRepository.RedisSession session)
SessionRepositorySession created by SessionRepository.createSession() is saved.
Some implementations may choose to save as the Session is updated by returning a Session that
immediately persists any changes. In this case, this method may not actually do anything.
save in interface SessionRepository<org.springframework.session.data.redis.RedisOperationsSessionRepository.RedisSession>session - the Session to save@Scheduled(cron="0 * * * * *") public void cleanupExpiredSessions()
public org.springframework.session.data.redis.RedisOperationsSessionRepository.RedisSession getSession(String id)
SessionRepositorySession by the Session.getId() or null if no Session is found.
If the Session extends ExpiringSession, then ExpiringSession.getLastAccessedTime() will be
updated on the returned object. In order to persist this change, SessionRepository.save(Session) must be invoked on the returned
instance.
getSession in interface SessionRepository<org.springframework.session.data.redis.RedisOperationsSessionRepository.RedisSession>id - the Session.getId() to lookupSession by the Session.getId() or null if no Session is found.public void delete(String sessionId)
SessionRepositorydelete in interface SessionRepository<org.springframework.session.data.redis.RedisOperationsSessionRepository.RedisSession>sessionId - the Session.getId() to deletepublic org.springframework.session.data.redis.RedisOperationsSessionRepository.RedisSession createSession()
SessionRepositorySession that is capable of being persisted by this SessionRepository.
This allows optimizations and customizations in how the Session is persisted. For example, the
implementation returned might keep track of the changes ensuring that only the delta needs to be persisted on
a save.
createSession in interface SessionRepository<org.springframework.session.data.redis.RedisOperationsSessionRepository.RedisSession>Session that is capable of being persisted by this SessionRepository