Why sharing session
- Enhance the user experience: If you had to change the way a machine can be the main core state when the user disconnects the user information Reply
- Provide high availability service: a server goes down to the user can be done almost no awareness, to provide a stable service available
Since the advent of the Internet era, the influx of a large number of Internet users, giving rise to a lot of stand-alone scenario can not be met, after all, concurrency and performance stand-alone has limitations. So they gave birth to distributed applications, the advent of distributed services is bound to solve all of the operations of each machine after a user logs on to the back-end distributed services are visible. If after the first operation request server A top hit, hit the second request server B above, a user operation to be performed on the same server AB are visible, but not to say that the second server B to log in and before repeating the operation has been treated thing again. After all, today's Internet is against the clock
Shared realization of session
Share realize the session, to be persistent session. Such as a common mysql, redis and so on. Then we look at springboot + springmvc shared achieve the session. We realize redis background to getting into the source code
automatically configured springboot for us ready environment, users need only a simple configuration can be used
- Dependent on configuration
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
- Configuration Properties
# 设置session刷新ON_SAVE(表示在response commit前刷新缓存),IMMEDIATE(表示只要有更新,就刷新缓存)
spring.session.redis.flush-mode=on_save
# 选择使用redis 作为session存储
spring.session.store-type=redis
Yes, that is a simple two configurations, we do share the session. Then we take a look at springboot automatic configuration are doing for us?
He spent the last article of the students should remember SessionHandler handle jetty services. Yes look at the name to know is a container used to process the session. Not seen or forget the students can follow the mass walk-wing doors ha ha https://blog.csdn.net/u010597819/article/details/90745546
seen students can follow a direct role we continue to look at SessionHandler handle
SessionHandler
Jetty container starts callback handler: doStart
- handler when the Server service starts vessel will also manage registration and start to destroy a variety of services and Handler's Filter, SessionHandler start doStart
- Gets Jetty Server Object
- If sessionCache cache is empty, initialization and caching
- Get SessionCacheFactory instance from the collection _beans Server instance management, if empty default implementation is used DefaultSessionCache
- Get SessionDataStoreFactory instance from the collection _beans Server instance management, if the default is empty achieve NullSessionDataStore
- If _sessionIdManager is empty
- Server objects get the sessionIdManager assignment, if still empty, create the default implementation DefaultSessionIdManager
- The collection _beans DefaultSessionIdManager registered to Server manages and start DefaultSessionIdManager.start
- Adding DefaultSessionIdManager to the current set of SessionHandler managed _beans
- Get Scheduler achieve from _beans Server manages the collection, if the default ScheduledExecutorScheduler is created empty and start start
- If not empty ContextHandler
- Get value from the value corresponding to the context org.eclipse.jetty.servlet.SessionCookie _initParams property map as cookie name, default: JSESSIONID
- Also acquired from the value corresponding to the value of org.eclipse.jetty.servlet.SessionIdPathParameterName _initParams properties as _sessionIdPathParameterName, _sessionIdPathParameterNamePrefix
- If the maximum equals -1 _maxCookieAge same org.eclipse.jetty.servlet.MaxAge attempt to obtain the corresponding value from the attribute value _initParams long as the cookie
- The same manner _sessionDomain, _sessionPath, _checkingRemoteSessionIdEncoding
- Creating a context SessionContext
- Initialize cache initialization _sessionCache
protected void doStart() throws Exception
{
//check if session management is set up, if not set up HashSessions
final Server server=getServer();
...
synchronized (server)
{
//Get a SessionDataStore and a SessionDataStore, falling back to in-memory sessions only
if (_sessionCache == null)
{
SessionCacheFactory ssFactory = server.getBean(SessionCacheFactory.class);
setSessionCache(ssFactory != null?ssFactory.getSessionCache(this):new DefaultSessionCache(this));
SessionDataStore sds = null;
SessionDataStoreFactory sdsFactory = server.getBean(SessionDataStoreFactory.class);
if (sdsFactory != null)
sds = sdsFactory.getSessionDataStore(this);
else
sds = new NullSessionDataStore();
_sessionCache.setSessionDataStore(sds);
}
if (_sessionIdManager==null)
{
_sessionIdManager=server.getSessionIdManager();
if (_sessionIdManager==null)
{
//create a default SessionIdManager and set it as the shared
//SessionIdManager for the Server, being careful NOT to use
//the webapp context's classloader, otherwise if the context
//is stopped, the classloader is leaked.
ClassLoader serverLoader = server.getClass().getClassLoader();
try
{
Thread.currentThread().setContextClassLoader(serverLoader);
_sessionIdManager=new DefaultSessionIdManager(server);
server.setSessionIdManager(_sessionIdManager);
server.manage(_sessionIdManager);
_sessionIdManager.start();
}
finally
{
Thread.currentThread().setContextClassLoader(_loader);
}
}
// server session id is never managed by this manager
addBean(_sessionIdManager,false);
}
_scheduler = server.getBean(Scheduler.class);
if (_scheduler == null)
{
_scheduler = new ScheduledExecutorScheduler();
_ownScheduler = true;
_scheduler.start();
}
}
// Look for a session cookie name
if (_context!=null)
{
String tmp=_context.getInitParameter(__SessionCookieProperty);
if (tmp!=null)
_sessionCookie=tmp;
tmp=_context.getInitParameter(__SessionIdPathParameterNameProperty);
if (tmp!=null)
setSessionIdPathParameterName(tmp);
// set up the max session cookie age if it isn't already
if (_maxCookieAge==-1)
{
tmp=_context.getInitParameter(__MaxAgeProperty);
if (tmp!=null)
_maxCookieAge=Integer.parseInt(tmp.trim());
}
// set up the session domain if it isn't already
if (_sessionDomain==null)
_sessionDomain=_context.getInitParameter(__SessionDomainProperty);
// set up the sessionPath if it isn't already
if (_sessionPath==null)
_sessionPath=_context.getInitParameter(__SessionPathProperty);
tmp=_context.getInitParameter(__CheckRemoteSessionEncoding);
if (tmp!=null)
_checkingRemoteSessionIdEncoding=Boolean.parseBoolean(tmp);
}
_sessionContext = new SessionContext(_sessionIdManager.getWorkerName(), _context);
_sessionCache.initialize(_sessionContext);
super.doStart();
}
Request Request chain handle callbacks: doScope
- Get SessionHandler Request request in the session and cookie
- If the child node _nextScope and _outerScope node, there is a recursive call
- doHandle call the next node handle
- Submit session: complete
- If the request is asynchronous and is requesting the establishment of Session Request asynchronous monitor SessionAsyncListener, waiting for the operation callback session
- If the request is not and is not a Request asynchronous request, submitted synchronization session: complete, cache to _sessionCache, if there is configured timeout timeout monitoring _sessionInactivityTimer create a scheduled task: SessionInactivityTimer
springboot automatic configuration SessionAutoConfiguration sharing session
- Start SessionAutoConfiguration automatic configuration after DataSourceAutoConfiguration, redis, such as automatic configuration
- Import SessionConfigurationImportSelector
- Introducing all storage type configuration StoreType
@Configuration
@ConditionalOnMissingBean(SessionRepository.class)
@ConditionalOnClass(Session.class)
@ConditionalOnWebApplication
@EnableConfigurationProperties(SessionProperties.class)
@AutoConfigureAfter({ DataSourceAutoConfiguration.class, HazelcastAutoConfiguration.class,
JdbcTemplateAutoConfiguration.class, MongoAutoConfiguration.class,
RedisAutoConfiguration.class })
@Import({ SessionConfigurationImportSelector.class, SessionRepositoryValidator.class })
public class SessionAutoConfiguration {
/**
* {@link ImportSelector} to add {@link StoreType} configuration classes.
*/
static class SessionConfigurationImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
StoreType[] types = StoreType.values();
String[] imports = new String[types.length];
for (int i = 0; i < types.length; i++) {
imports[i] = SessionStoreMappings.getConfigurationClass(types[i]);
}
return imports;
}
}
/**
* Bean used to validate that a {@link SessionRepository} exists and provide a
* meaningful message if that's not the case.
*/
static class SessionRepositoryValidator {
...
}
}
RedisSessionConfiguration配置
- SpringBootRedisHttpSessionConfiguration configuration, and the configuration of its parent
- SessionProperties injection configuration (prefix spring.session), the configuration session, redis namespace namespace configuration, flushmode configuration
- SpringHttpSessionConfiguration配置
- Cookie serialization implementation, the default is DefaultCookieSerializer
- SessionRepositoryFilter, session repository filter configuration, all interception SessionRepository repository, Servlet.Filter This filter implements the interface, the session data is requested container operable to perform persistence shared
- RedisHttpSessionConfiguration配置
- EnableScheduling start classes scheduled task support, if cleanupCron is not configured by default to clean up the expression: "0 * * * * *", that is, 0 seconds every minute of time will clear up once
- Create the configuration repository RedisOperationsSessionRepository
- Create a configuration RedisMessageListenerContainer, listening session to create, delete, overtime
- Create a connection factory configuration redis
- redis task executor injection springSessionRedisTaskExecutor
- 注入springSessionRedisSubscriptionExecutor
- Introducing CI EnableRedisHttpSession annotation configuration and current configuration is disposed cover: maxInactiveIntervalInSeconds, redisNamespace, redisFlushMode, cleanupCron
- Configuration regular cleaning task, cleaning timeout session
Session created
Cas services use the company's services, the stack below, you can see the acquisition session, getSession by cas interceptors at login, if the session does not exist, create session
java.lang.Thread.State: RUNNABLE
at org.springframework.session.web.http.SessionRepositoryFilter$SessionRepositoryRequestWrapper.getSession(SessionRepositoryFilter.java:391)
at org.springframework.session.web.http.SessionRepositoryFilter$SessionRepositoryRequestWrapper.getSession(SessionRepositoryFilter.java:217)
at javax.servlet.http.HttpServletRequestWrapper.getSession(HttpServletRequestWrapper.java:279)
at javax.servlet.http.HttpServletRequestWrapper.getSession(HttpServletRequestWrapper.java:279)
at org.pac4j.core.context.session.J2ESessionStore.getHttpSession(J2ESessionStore.java:24)
at org.pac4j.core.context.session.J2ESessionStore.get(J2ESessionStore.java:34)
at org.pac4j.core.context.session.J2ESessionStore.get(J2ESessionStore.java:19)
at org.pac4j.core.context.WebContext.getSessionAttribute(WebContext.java:94)
at com.....authentication.filters.CasSecurityFilter.doFilter(CasSecurityFilter.java:62)
SessionRepositoryFilter.SessionRepositoryRequestWrapper.getSession
- The current scene baseRequest packaged as SessionRepositoryRequestWrapper, getSession (boolean create) is rewritten
- If the current currentSession not directly return empty, otherwise continue
- Get sessionId, SessionRepositoryFilter.getRequestedSessionId, acquired in accordance with the strategy session httpSessionStrategy
- Obtaining session from the session repository SessionRepositoryFilter.this.sessionRepository (RedisOperationsSessionRepository) according to sessionId, read redis
- If the session is to identify and create space for false return null
- If the session identifier and creates empty session is created and is true for the packages returned HttpSessionWrapper
- session repository created session: RedisOperationsSessionRepository.createSession
SessionRepositoryFilter(springSessionRepositoryFilter)
doFilter parent OncePerRequestFilter the callback class method doFilterInternal
- Use SessionRepositoryRequestWrapper packaging request
- SessionRepositoryResponseWrapper packaging after use request and response package
- _HttpSessionStrategy_ policy (e.g.: CookieHttpSessionStrategy) the request and response package after packaging HttpServletRequest, HttpServletResponse
- Call filtering chain
- Submit session: SessionRepositoryRequestWrapper.commitSession, using _HttpSessionStrategy policies written response to sessionId and persistence session_
to sum up
sharing session : spring SessionRepositoryFilter through the filter will encapsulate the Request Type Request Create request session, session selection policy from the repository _RedisOperationsSessionRepository _HttpSessionStrategy session for persistence of the session is shared SessionRepositoryRequestWrapper type. Of course, there is the case of our sessionHandler cas If no other configurations default to session no action that is NullSessionDataStore
Jetty properties doScope, doHandle
* <p>For example if Scoped handlers A, B & C were chained together, then
* the calling order would be:</p>
* <pre>
* A.handle(...)
* A.doScope(...)
* B.doScope(...)
* C.doScope(...)
* A.doHandle(...)
* B.doHandle(...)
* C.doHandle(...)
* </pre>
*
* <p>If non scoped handler X was in the chained A, B, X & C, then
* the calling order would be:</p>
* <pre>
* A.handle(...)
* A.doScope(...)
* B.doScope(...)
* C.doScope(...)
* A.doHandle(...)
* B.doHandle(...)
* X.handle(...)
* C.handle(...)
* C.doHandle(...)
* </pre>