浅谈Tomcat Session管理分析

2019-10-18 19:52:32刘景俊

sessionId不为空的情况下,会先比较sessionId是否等于currentSessionId中的sessionId,如果等于则从currentSession中取出session,currentSessionId和currentSession都是ThreadLocal变量,这里并没有直接从redis里面取数据,如果同一线程没有去处理其他用户信息,是可以直接从内存中取出的,提高了性能;最后才从redis里面获取数据,从redis里面获取的是一段二进制数据,需要进行反序列化操作,相关序列化和反序列化都在JavaSerializer类中:

public void deserializeInto(byte[] data, RedisSession session, SessionSerializationMetadata metadata)
    throws IOException, ClassNotFoundException {
  BufferedInputStream bis = new BufferedInputStream(new ByteArrayInputStream(data));
  Throwable arg4 = null;
 
  try {
    CustomObjectInputStream x2 = new CustomObjectInputStream(bis, this.loader);
    Throwable arg6 = null;
 
    try {
      SessionSerializationMetadata x21 = (SessionSerializationMetadata) x2.readObject();
      metadata.copyFieldsFrom(x21);
      session.readObjectData(x2);
    } catch (Throwable arg29) {
  ......
}

二进制数据中保存了2个对象,分别是SessionSerializationMetadata和RedisSession,SessionSerializationMetadata里面保存的是Session中的attributes信息,RedisSession其实也有attributes数据,相当于这份数据保存了2份;

2.如何创建

同样RedisSessionManager重写了createSession方法,2个重要的点分别:sessionId的唯一性问题和session保存到redis中;

// Ensure generation of a unique session identifier.
if (null != requestedSessionId) {
 sessionId = sessionIdWithJvmRoute(requestedSessionId, jvmRoute);
 if (jedis.setnx(sessionId.getBytes(), NULL_SESSION) == 0L) {
  sessionId = null;
 }
} else {
 do {
  sessionId = sessionIdWithJvmRoute(generateSessionId(), jvmRoute);
 } while (jedis.setnx(sessionId.getBytes(), NULL_SESSION) == 0L); // 1 = key set; 0 = key already existed
}

分布式环境下有可能出现生成的sessionId相同的情况,所以需要确保唯一性;保存session到redis中是最核心的一个方法,何时更新,何时过期都在此方法中处理;

3.何时更新到redis

具体看saveInternal方法

protected boolean saveInternal(Jedis jedis, Session session, boolean forceSave) throws IOException {
  Boolean error = true;
 
  try {
   log.trace("Saving session " + session + " into Redis");
 
   RedisSession redisSession = (RedisSession)session;
 
   if (log.isTraceEnabled()) {
    log.trace("Session Contents [" + redisSession.getId() + "]:");
    Enumeration en = redisSession.getAttributeNames();
    while(en.hasMoreElements()) {
     log.trace(" " + en.nextElement());
    }
   }
 
   byte[] binaryId = redisSession.getId().getBytes();
 
   Boolean isCurrentSessionPersisted;
   SessionSerializationMetadata sessionSerializationMetadata = currentSessionSerializationMetadata.get();
   byte[] originalSessionAttributesHash = sessionSerializationMetadata.getSessionAttributesHash();
   byte[] sessionAttributesHash = null;
   if (
      forceSave
      || redisSession.isDirty()
      || null == (isCurrentSessionPersisted = this.currentSessionIsPersisted.get())
      || !isCurrentSessionPersisted
      || !Arrays.equals(originalSessionAttributesHash, (sessionAttributesHash = serializer.attributesHashFrom(redisSession)))
     ) {
 
    log.trace("Save was determined to be necessary");
 
    if (null == sessionAttributesHash) {
     sessionAttributesHash = serializer.attributesHashFrom(redisSession);
    }
 
    SessionSerializationMetadata updatedSerializationMetadata = new SessionSerializationMetadata();
    updatedSerializationMetadata.setSessionAttributesHash(sessionAttributesHash);
 
    jedis.set(binaryId, serializer.serializeFrom(redisSession, updatedSerializationMetadata));
 
    redisSession.resetDirtyTracking();
    currentSessionSerializationMetadata.set(updatedSerializationMetadata);
    currentSessionIsPersisted.set(true);
   } else {
    log.trace("Save was determined to be unnecessary");
   }
 
   log.trace("Setting expire timeout on session [" + redisSession.getId() + "] to " + getMaxInactiveInterval());
   jedis.expire(binaryId, getMaxInactiveInterval());
 
   error = false;
 
   return error;
  } catch (IOException e) {
   log.error(e.getMessage());
 
   throw e;
  } finally {
   return error;
  }
 }