In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-01-29 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Internet Technology >
Share
Shulou(Shulou.com)06/02 Report--
In this issue, the editor will bring you about the principle of the event mechanism in spring-session. The article is rich in content and analyzes and narrates it from a professional point of view. I hope you can get something after reading this article.
Gradually go deep into the exploration of the principle of event mechanism in Spring-Session. As we all know, the Servlet specification has the handling of HttpSession events, such as: HttpSessionEvent/HttpSessionIdListener/HttpSessionListener, you can see Package javax.servlet
There is also a corresponding Session event mechanism implementation in Spring-Session, including Session create / expiration / delete events.
This paper mainly explores the event mechanism in Spring-Session from the following aspects.
Abstraction of Session events
Trigger mechanism of event
Note:
The event triggering mechanism here only introduces RedissSession-based implementations. MapSession based on memory Map implementation does not support the Session event mechanism. Other Session implementations are not concerned here either.
I. abstraction of session events
Let's first take a look at the abstract UML class diagram of Session events to grasp the dependencies between events as a whole.
The top level of Session Event is ApplicationEvent, the Spring context event object. It can be seen that the event mechanism of Spring-Session is based on the implementation of Spring context events.
The abstract AbstractSessionEvent event object provides the access to Session (in this case, the object of Spring Session) and SessionId.
Based on the type of event, it is classified as:
Session creation event
Session delete event
Session expiration event
Tips:
The Session destroy event is only the unity of deletion and expiration events and has no practical meaning.
The event object is just an abstraction of the event itself, describing the properties of the event, such as:
Get the source of the event: getSource gets the source of the event
Get the corresponding event property: getSession/getSessoinId gets the Session associated with the time
Let's take a closer look at how the above Session events are triggered and analyze the event flow process from the event source to the event listener.
two。 Trigger mechanism of event
Before reading this section, the reader should know Redis's Pub/Sub and KeySpace Notification, if not already, the portal Redis Keyspace Notifications and Pub/Sub.
The ApplicationEvent implementation of Session Event events based on Spring was also introduced in the previous section. Let's start with a brief understanding of the spring context event mechanism:
The ApplicationEventPublisher implementation is used to publish the Spring context event ApplicationEvent
The ApplicationListener implementation is used to listen for Spring context event ApplicationEvent
ApplicationEvent Abstract context event
Then the Spring-Session must include event publishers ApplicationEventPublisher publish Session events and ApplicationListener listening Session events.
You can see that ApplicationEventPublisher publishes an event:
@ FunctionalInterfacepublic interface ApplicationEventPublisher {/ * * Notify all matching listeners registered with this * application of an application event. Events may be framework events * (such as RequestHandledEvent) or application-specific events. * @ param event the event to publish * @ see org.springframework.web.context.support.RequestHandledEvent * / default void publishEvent (ApplicationEvent event) {publishEvent ((Object) event);} / * * Notify all matching listeners registered with this * application of an event. *
If the specified {@ code event} is not an {@ link ApplicationEvent}, * it is wrapped in a {@ link PayloadApplicationEvent}. * @ param event the event to publish * @ since 4.2 * @ see PayloadApplicationEvent * / void publishEvent (Object event);}
ApplicationListener is used to listen for the corresponding events:
@ FunctionalInterfacepublic interface ApplicationListener extends EventListener {/ * * Handle an application event. * @ param event the event to respond to * / void onApplicationEvent (E event);}
Tips:
The publish / subscribe mode is used here, event listeners can listen to events of interest, and publishers can publish various events. However, this is an internal publish subscription, that is, the observer mode.
The process implementation of the Session event is as follows:
The above figure shows the Spring-Session event flowchart, the event source comes from the spring-data-redis key space notification, abstract MessageListener listens on the Redis event source in the Redi project, and then propagates it to the spring application context publisher, who publishes the event. The listener Listener in the spring context can listen to the Session event.
Because both are ApplicationEvent support for Spring provided by the Spring framework. Session Event is implemented based on ApplicationEvent, and there must be implementations of corresponding publishers and listeners.
The SessionRepository of RedisSession in Spring-Session is RedisOperationSessionRepository. All management operations about RedisSession are implemented by it, so the source of Session is RedisOperationSessionRepository.
Holding the ApplicationEventPublisher object in RedisOperationSessionRepository is used to publish Session events.
Private ApplicationEventPublisher eventPublisher = new ApplicationEventPublisher () {@ Override public void publishEvent (ApplicationEvent event) {} @ Override public void publishEvent (Object event) {}}
However, the ApplicationEventPublisher is an empty implementation, and the actual implementation is automatically configured by Spring-Session when the application is started. There is information in RedisHttpSessionConfiguration in the spring-session-data-redis module that when creating a RedisOperationSessionRepository Bean, the set method is called to configure the ApplicationEventPublisher.
Configuration@EnableSchedulingpublic class RedisHttpSessionConfiguration extends SpringHttpSessionConfiguration implements BeanClassLoaderAware, EmbeddedValueResolverAware, ImportAware, SchedulingConfigurer {private ApplicationEventPublisher applicationEventPublisher; @ Bean public RedisOperationsSessionRepository sessionRepository () {RedisTemplate redisTemplate = createRedisTemplate (); RedisOperationsSessionRepository sessionRepository = new RedisOperationsSessionRepository (redisTemplate); / / injection dependency sessionRepository.setApplicationEventPublisher (this.applicationEventPublisher); if (this.defaultRedisSerializer! = null) {sessionRepository.setDefaultSerializer (this.defaultRedisSerializer) } sessionRepository .setDefaultMaxInactiveInterval (this.maxInactiveIntervalInSeconds); if (StringUtils.hasText (this.redisNamespace)) {sessionRepository.setRedisKeyNamespace (this.redisNamespace);} sessionRepository.setRedisFlushMode (this.redisFlushMode); return sessionRepository;} / / ApplicationEventPublisher Bean @ Autowired public void setApplicationEventPublisher (ApplicationEventPublisher applicationEventPublisher) in the injection context {this.applicationEventPublisher = applicationEventPublisher }}
During automatic configuration, the injection of the ApplicationEventPublisher in the context is actually the ApplicationContext object.
Note:
Consider the length of the above RedisHttpSessionConfiguration to show clips.
For ApplicationListener, it is implemented by application developers themselves, and you can register as Bean. When a Session Event is released, it can be monitored.
/ * * session event listener * * @ author huaijin * / @ Componentpublic class SessionEventListener implements ApplicationListener {private static final String CURRENT_USER = "currentUser"; @ Override public void onApplicationEvent (SessionDeletedEvent event) {Session session = event.getSession (); UserVo userVo = session.getAttribute (CURRENT_USER); System.out.println ("Current session's user:" + userVo.toString ());}}
The above section explores the publishers and listeners of Session events, but the trigger release of core events is triggered by Redis's keyspace notification mechanism, which notifies Spring-Session applications when Session is created / deleted / expired.
RedisOperationsSessionRepository implements the MessageListener interface in spring-data-redis.
/ * Listener of messages published in Redis. * * @ author Costin Leau * @ author Christoph Strobl * / public interface MessageListener {/ * * Callback for processing received objects through Redis. * * @ param message message must not be {@ literal null}. * @ param pattern pattern matching the channel (if specified)-can be {@ literal null}. * / void onMessage (Message message, @ Nullable byte [] pattern);}
This listener is used to listen for messages published by redis. RedisOperationsSessionRepositorys implements the Redis space message notification listener interface, which is implemented as follows:
Public class RedisOperationsSessionRepository implements FindByIndexNameSessionRepository, MessageListener {@ Override @ SuppressWarnings ("unchecked") public void onMessage (Message message, byte [] pattern) {/ / get the redis channel channel byte [] messageChannel = message.getChannel () published by the message; / / get the body content of the message byte [] messageBody = message.getBody (); String channel = new String (messageChannel) / / if Session creates a channel to publish the message, it is the Session creation event if (channel.startsWith (getSessionCreatedChannelPrefix () {/ / load Session Map loaded = (Map) this.defaultSerializer .normalialize (message.getBody ()) from the message body; / / publish the creation event handleCreated (loaded, channel) Return;} / / if the message body is not prefixed with an expired key, it is returned directly. Because of the key naming convention of spring-session in redis: / / "${namespace}: sessions:expires:$ {sessionId}", such as: / / session.example:sessions:expires:a5236a19-7325-4783-b1f0-db9d4442db9a / /, determine whether the expired or deleted key is an expired key of spring-session. If not, it may be the operation of other keys in the application, so directly return String body = new String (messageBody); if (! body.startsWith (getExpiredKeyPrefix () {return;} / / judge the event type of the key space del or expire time boolean isDeleted = channel.endsWith (": del") according to channel. If (isDeleted | | channel.endsWith (": expired")) {int beginIndex = body.lastIndexOf (":") + 1; int endIndex = body.length () / / Redis key space message notifies the key that the content is the operation key. The naming rule in the spring-session key is: / / "${namespace}: sessions:expires:$ {sessionId}". The following is parsing sessionId String sessionId = body.substring (beginIndex, endIndex) according to the rule; / / loading session RedisSession session = getSession (sessionId, true) according to sessionId If (session = = null) {logger.warn ("Unable to publish SessionDestroyedEvent for session" + sessionId); return;} if (logger.isDebugEnabled ()) {logger.debug ("Publishing SessionDestroyedEvent for session" + sessionId);} cleanupPrincipalIndex (session) / / publish Session delete event if (isDeleted) {handleDeleted (session);} else {/ / otherwise publish Session expire event handleExpired (session);}
The next step is to delve deeper into the past life and this life produced by each event.
Trigger of 1.Session creation event
RedisOperationSessionRepository publishes a message to Redis specified channel ${namespace}: event:created:$ {sessionId}
The implementation of MessageListener RedisOperationSessionRepository listens to the message of Redis specified channel ${namespace}: event:created:$ {sessionId}
Propagate it to ApplicationEventPublisher
ApplicationEventPublisher publishes SessionCreateEvent
ApplicationListener listens to SessionCreateEvent and executes the corresponding logic
When a Session is saved in RedisOperationSessionRepository, it is determined whether the Session is newly created.
If newly created, to the
@ Overridepublic void save (RedisSession session) {session.saveDelta (); / / determine whether it is the newly created session if (session.isNew ()) {/ / get the channel:$ {namespace}: event:created:$ {sessionId} specified by redis, / / for example: session.example:event:created:82sdd-4123-o244-ps123 String sessionCreatedKey = getSessionCreatedChannel (session.getId ()) / / publish session data this.sessionRedisOperations.convertAndSend (sessionCreatedKey, session.delta) to the channel; / / set session to non-newly created session.setNew (false);}}
The call to this save method is submitted by HttpServletResponse-- that is, it returns a client-side response call, which has been explained in detail in the previous article and will not be repeated here. The RedisOperationSessionRepository implementation of MessageListener has been introduced above, and I won't repeat it here again.
Note:
It's a little roundabout here. Personally, I think that RedisOperationSessionRepository publishes, creates and then listens itself, mainly considering the handling of SessionCreateEvent events in a distributed or clustered environment.
Trigger of 2.Session delete event
Tips:
Redis KeySpace Notification is used in the deletion event, so it is recommended to understand this technology first.
The expired key of the specified Session in the Redis key space is deleted by RedisOperationSessionRepository, and the Redi key space publishes the delete event message to the channel of * * _ keyevent@*:del**
The implementation of MessageListener RedisOperationSessionRepository listens to the message of Redis specified channel * * _ _ keyevent@*:del**
Propagate it to ApplicationEventPublisher
ApplicationEventPublisher publishes SessionDeleteEvent
ApplicationListener listens to SessionDeleteEvent and executes the corresponding logic
When the invalidate method of HttpSession is called to invalidate Session, the deleteById method of RedisOperationSessionRepository is called to delete the expired key of Session.
/ * Allows creating an HttpSession from a Session instance. * * @ author Rob Winch * @ since 1.0 * / private final class HttpSessionWrapper extends HttpSessionAdapter {HttpSessionWrapper (S session, ServletContext servletContext) {super (session, servletContext);} @ Override public void invalidate () {super.invalidate (); SessionRepositoryRequestWrapper.this.requestedSessionInvalidated = true; setCurrentSession (null); clearRequestedSessionCache (); / / call delete method SessionRepositoryFilter.this.sessionRepository.deleteById (getId ()) }}
In the previous article, I introduced the packaging Spring Session as HttpSession, so I won't repeat it here. Here we focus on the analysis of deleteById content:
@ Overridepublic void deleteById (String sessionId) {/ / return RedisSession session = getSession (sessionId, true) if session is empty; if (session = = null) {return;} cleanupPrincipalIndex (session); this.expirationPolicy.onDelete (session); / / get session's expiration key String expireKey = getExpiredKey (session.getId ()) / / delete the expired key, and the del event message is generated in the redised key space, which is monitored by MessageListener that is / / RedisOperationSessionRepository this.sessionRedisOperations.delete (expireKey); session.setMaxInactiveInterval (Duration.ZERO); save (session);}
The subsequent process is the same as the SessionCreateEvent process.
Trigger of 3.Session failure event
The expired event flow of Session is special, because of the particularity of Redis keyspace notification, Redi key space notification can not guarantee the timeliness of expired key notification.
There is a scheduled task method in RedisOperationsSessionRepository that accesses the expired sessionId in the set of expired keys in the integral Session, such as spring:session:expirations:1439245080000. Triggering the REDIS key space will issue an expired event message to the channel of * * _ _ keyevent@*:expired**
The implementation of MessageListener RedisOperationSessionRepository listens to the message of Redis specified channel * * _ _ keyevent@*:expired**
Propagate it to ApplicationEventPublisher
ApplicationEventPublisher publishes SessionDeleteEvent
ApplicationListener listens to SessionDeleteEvent and executes the corresponding logic
@ Scheduled (cron = "0 *") public void cleanupExpiredSessions () {this.expirationPolicy.cleanExpiredSessions ();}
The cleanExpiredSessions method is executed for every whole minute of the scheduled task. ExpirationPolicy is an RedisSessionExpirationPolicy instance and a RedisSession expiration policy.
Public void cleanExpiredSessions () {/ / get the current timestamp long now = System.currentTimeMillis (); / / the time scrolls to the whole minute, removing the seconds and milliseconds part long prevMin = roundDownMinute (now); if (logger.isDebugEnabled ()) {logger.debug ("Cleaning up sessions expiring at" + new Date (prevMin)) } / / get the expired key set according to the whole time, such as: spring:session:expirations:1439245080000 String expirationKey = getExpirationKey (prevMin); / / get all expired session Set sessionsToExpire = this.redis.boundSetOps (expirationKey). Members (); / / delete expired session key set this.redis.delete (expirationKey) / / touch accesses all expired session and triggers for key space notification message for (Object session: sessionsToExpire) {String sessionKey = getSessionKey ((String) session); touch (sessionKey);}}
Scroll the timestamp to the whole minute
Static long roundDownMinute (long timeInMs) {Calendar date = Calendar.getInstance (); date.setTimeInMillis (timeInMs); / / Clean second and millisecond bit date.clear (Calendar.SECOND); date.clear (Calendar.MILLISECOND); return date.getTimeInMillis ();}
Get the collection of expired Session
String getExpirationKey (long expires) {return this.redisSession.getExpirationsKey (expires);} / for example: spring:session:expirations:1439245080000String getExpirationsKey (long expiration) {return this.keyPrefix + "expirations:" + expiration;}
Call the Exists command of Redis to access the expired session key and trigger the Redis key space message.
/ * * By trying to access the session we only trigger a deletion if it the TTL is * expired. This is done to handle * https://github.com/spring-projects/spring-session/issues/93 * * @ param key the key * / private void touch (String key) {this.redis.hasKey (key);}
So far, Spring-Session 's Session event notification module is clear:
Redis keyspace Session event source: Session create channel / Session delete channel / Session expired channel
The RedisOperationsSessionRepository message listener in Spring-Session listens to the event type of Redis
RedisOperationsSessionRepository is responsible for spreading it to ApplicationEventPublisher.
ApplicationEventPublisher wraps it as a Session Event publication of type ApplicationEvent
ApplicationListener listens to Session Event and handles the corresponding logic
These are the principles of the event mechanism in spring-session shared by the editor. If you happen to have similar doubts, you might as well refer to the above analysis to understand. If you want to know more about it, you are welcome to follow the industry information channel.
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.