Backend Development 12 min read

Spring Session Implementation Guide: Session Sharing with Redis in Distributed Systems

Spring Session enables distributed session sharing by storing HTTP session data in Redis, using a filter and listener configuration to replace Tomcat’s in‑memory storage, managing three Redis keys with coordinated expirations, and subscribing to keyspace events for reliable cleanup and cross‑instance access.

vivo Internet Technology
vivo Internet Technology
vivo Internet Technology
Spring Session Implementation Guide: Session Sharing with Redis in Distributed Systems

HTTP protocol is stateless by design. To maintain session information, browsers use cookies with SessionID to identify session requests, and servers store session data using SessionID as the key. In single-instance applications, sessions can be stored in the application process itself. However, as application scale grows and horizontal scaling is required, multi-instance session sharing becomes a challenge.

Spring Session is designed to solve the multi-process session sharing problem. This article explains how to use Spring Session and its working principles.

1. Background

When applications are deployed on Tomcat, sessions are maintained in Tomcat's memory. If an application is deployed on multiple instances, sessions cannot be shared. Spring Session solves the session sharing problem in distributed scenarios.

2. Usage Method

Spring Session supports storage in Hazelcast, Redis, MongoDB, and relational databases. This article focuses on Redis storage.

web.xml configuration:

springSessionRepositoryFilter
org.springframework.web.filter.DelegatingFilterProxy
springSessionRepositoryFilter
/*

Spring main configuration:

p:poolConfig-ref="jedisPoolConfig"

3. Working Process

Tomcat web.xml parsing sequence: Listener → Filter → Servlet.

1) SessionRepositoryFilter is loaded into the Spring container through Tomcat's listener. The RedisHttpSessionConfiguration in the Spring configuration file generates SessionRepositoryFilter in its parent class SpringHttpSessionConfiguration:

@Bean
public
SessionRepositoryFilter
springSessionRepositoryFilter(
        SessionRepository
sessionRepository) {
    ......
    return sessionRepositoryFilter;
}

2) Filter initialization: The filter configured in web.xml is DelegatingFilterProxy. DelegatingFilterProxy retrieves the springSessionRepositoryFilter from the Spring container during initialization:

protected void initFilterBean() throws ServletException {
        synchronized (this.delegateMonitor) {
            if (this.delegate == null) {
                if (this.targetBeanName == null) {
                    this.targetBeanName = getFilterName();
                }
                WebApplicationContext wac = findWebApplicationContext();
                if (wac != null) {
                    this.delegate = initDelegate(wac);
                }
            }
        }
    }

SessionRepositoryFilter core workflow:

protected void doFilterInternal(HttpServletRequest request,
           HttpServletResponse response, FilterChain filterChain)
           throws ServletException, IOException {
       request.setAttribute(SESSION_REPOSITORY_ATTR, this.sessionRepository);
       //Wraps HttpServletRequest, overrides getSession(boolean create) method
       SessionRepositoryRequestWrapper wrappedRequest = new SessionRepositoryRequestWrapper(
               request, response, this.servletContext);
       ......
       try {
           filterChain.doFilter(strategyRequest, strategyResponse);
       }
       finally {
           //Ensure session persistence
           wrappedRequest.commitSession();
       }
   }

4. Caching Mechanism

Each session is stored in Redis with three keys:

spring:session:sessions - Hash structure storing main session content (creationTime, maxInactiveInterval, lastAccessedTime)

spring:session:sessions:expires - String structure storing an empty value

spring:session:expirations - Set structure storing session IDs that expire at specific timestamps

Redis has three key expiration strategies: timed deletion, lazy deletion, and periodic deletion. Redis uses lazy deletion and periodic deletion strategies. Relying solely on Redis's expiration policy for real-time key deletion is unreliable.

Spring Session uses three keys with different expiration times to ensure that when the expiration event is triggered, the session data can still be retrieved. The spring:session:sessions:expires key expires 5 minutes earlier than the other keys, allowing business logic to access session information after expiration.

Session cleanup scheduled task:

public void cleanExpiredSessions() {
        long now = System.currentTimeMillis();
        long prevMin = roundDownMinute(now);
        ......
        String expirationKey = getExpirationKey(prevMin);
        Set
sessionsToExpire = this.redis.boundSetOps(expirationKey).members();
        this.redis.delete(expirationKey);
        for (Object session : sessionsToExpire) {
            String sessionKey = getSessionKey((String) session);
            touch(sessionKey);
        }
    }
    
    private void touch(String key) {
        //Access the key to trigger lazy deletion
        this.redis.hasKey(key);
    }

5. Event Subscription

By default, Spring Session subscribes to keyspace notifications (gxE). ConfigureNotifyKeyspaceEventsAction enables keyspace notifications:

public void configure(RedisConnection connection) {
       String notifyOptions = getNotifyOptions(connection);
       String customizedNotifyOptions = notifyOptions;
       if (!customizedNotifyOptions.contains("E")) {
           customizedNotifyOptions += "E";
       }
       boolean A = customizedNotifyOptions.contains("A");
       if (!(A || customizedNotifyOptions.contains("g"))) {
           customizedNotifyOptions += "g";
       }
       if (!(A || customizedNotifyOptions.contains("x"))) {
           customizedNotifyOptions += "x";
       }
       if (!notifyOptions.equals(customizedNotifyOptions)) {
           connection.setConfig(CONFIG_NOTIFY_KEYSPACE_EVENTS, customizedNotifyOptions);
       }
   }

RedisHttpSessionConfiguration registers event listeners:

@Bean
 public RedisMessageListenerContainer redisMessageListenerContainer(
         RedisConnectionFactory connectionFactory,
         RedisOperationsSessionRepository messageListener) {
     ......
     //Subscribe to del and expired events
     container.addMessageListener(messageListener,
             Arrays.asList(new PatternTopic("__keyevent@*:del"),
                     new PatternTopic("__keyevent@*:expired")));
     //Subscribe to created events
     container.addMessageListener(messageListener, Arrays.asList(new PatternTopic(
             messageListener.getSessionCreatedChannelPrefix() + "*")));
     return container;
 }

Event subscription example:

@Component
public class SessionExpiredListener implements ApplicationListener
{
    @Override
    public void onApplicationEvent(SessionExpiredEvent event) {
        ......
    }
}

6. Summary

Spring Session provides an excellent solution for resource sharing in distributed environments. Based on Servlet specifications, it requires only simple configuration to implement session sharing, achieving low coupling with business logic. This design philosophy can be referenced in future project development.

Distributed SystemsJavaBackend DevelopmentRedisSession Managementsession sharingspring session
vivo Internet Technology
Written by

vivo Internet Technology

Sharing practical vivo Internet technology insights and salon events, plus the latest industry news and hot conferences.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.