1. 程式人生 > >redis實現分散式session共享

redis實現分散式session共享

在講解redis分散式session共享之前,我們先聊聊tomcat中session管理機制,包括:請求過程中session操作,sessionid解析過程,servlet獲取session流程,以及tomca中session的管理機制。

Tomcat session管理機制

請求過程中的session操作:

在請求過程中首先要解析請求中的sessionid資訊,然後將sessionid儲存到request的引數列表中。然後再從request獲取session的時候,如果存在sessionid那麼就根據id從session池中獲取session,如果sessionid不存在或者sesssion失效,那麼則新建session並且將session資訊放入session池,供下次使用。

sessionid解析過程(web工程引入tomcat jar包可debug除錯):

使用者傳送一個http請求傳遞給Http11Processor,經由Http11Processor解析封裝在org.apache.coyote.Request然後傳遞給CoyoteAdapter,CoyoteAdapter是一個介面卡,將coyote框架封裝的org.apache.coyote.Request適配給org.apache.catalina.connector.Request,轉換完之後會呼叫parsePathParameters方法區解析路徑引數中的cookie資訊,先嚐試從url中解析出sessionid,然後會呼叫parseSessionCookiesId,這個就是從cookie中解析sessionid存在request。解析到sessionid就放到了request裡面,解析sessionid的邏輯就ok了。

servlet獲取session的流程:

servlet通過request獲取session的時候,其實呼叫的是HttpServletRequest(其實是RequestFacade,封裝了Request的一個門面),然後RequestFacade會呼叫真是得Request的getSession方法。Request具體邏輯是呼叫Context容器的getManger方法獲取session管理器,然後如果sessionid如果被解析出來了,那麼則會呼叫findSession方法從session物件池中獲取對應的session,反之如果sessionid不存在,則需要重新建立一個session,並放入session物件池中。

session管理機制:

session管理器元件負責管理session物件,eg:常見和銷燬session物件
(1)Manager:定義了關聯到某一個容器的用來管理session池的基本介面。
(2)ManagerBase:實現了Manager介面,該類提供了Session管理器的常見功能的實現。
(3)StandardManager:繼承自ManagerBase,tomcat的預設Session管理器(不指定配置,預設使用這個),是tomcat處理session的非叢集實現(也就說是單機版的),tomcat關閉時,記憶體session資訊會持久化到磁碟儲存為SESSION.ser,再次啟動時恢復。
(4)PersistentManagerBase:繼承自ManagerBase,實現了和定義了session管理器持久化的基礎功能。
(5) PersistentManager:繼承自PersistentManagerBase,主要實現的功能是會把空閒的會話物件(通過設定超時時間)交換到磁碟上。
(6)ClusterManager:實現了Manager介面,通過類名應該能猜到,這個就是管理叢集session的管理器和上面那個StandardManager單機版的session管理器是相對的概念。這個類定義類叢集間session的複製共享介面。
(7)ClusterManagerBase:實現了ClusterManager介面,繼承自ManagerBase。該類實現了session複製的基本操作。
(8)BackupManager:繼承自ClusterManagerBase, 叢集間session複製策略的一種實現,會話資料只有一個備份節點,這個備份節點的位置叢集中所有節點都可見。這種設計使它有個優勢就是支援異構部署。
(9)DeltaManager:繼承自ClusterManagerBase,叢集建session複製策略的一種實現,和BackupManager不同的是,會話資料會複製到叢集中所有的成員節點,這也就要求叢集中所有節點必須同構,必須部署相同的應用。

Redis分散式session配置


分散式session原理


如上圖,多例項下可以使用redis實現分散式session管理,客戶端請求,經過負載均衡分發至tomcat例項,再經過session管理,實現session在redis中存取。配置redis主從叢集,主redis資料熱備份至從redis,當主redis宕機了,系統自動切換至從redis,從而保證系統快取方面高可用。

一言不合上程式碼,就是任性...

  1. redis配置
    可以使用spring-redis.xml作為redis配置檔名,首先配置redis快取池:
    <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">  
    <property name="maxTotal" value="${redis.pool.maxTotal}"/>   
    <property name="maxIdle" value="${redis.pool.maxIdle}"/>  
    <property name="maxWaitMillis" value="${redis.pool.maxWaitMillis}"/>    
    <property name="testOnBorrow" value="${redis.pool.testOnBorrow}"/>
    </bean>
    再配置redis模板引擎redisTemplat:
    <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">  
    <property name="connectionFactory">        
    <bean class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"              p:hostName="${redis.host.business}" p:port="${redis.port.business}"              p:database="1"  p:password="${redis.password.business}"              p:poolConfig-ref="jedisPoolConfig" />    </property>    
    <property name="defaultSerializer" ref="stringRedisSerializer" />    
    <property name="keySerializer" ref="stringRedisSerializer" />   
    <property name="valueSerializer" ref="jdkSerializationRedisSerializer" />    
    <property name="hashValueSerializer" ref="jdkSerializationRedisSerializer" />
    </bean>
  2. 配置分散式session過濾器
    <filter>   
      <filter-name>springSessionFilter</filter-name>    
      <filterclass>org.springframework.web.filter.DelegatingFilterProx   </filter-class>    
      <init-param>        
            <param-name>targetBeanName</param-name>       
            <param-value>springSession</param-value>    
      </init-param>
    </filter>
    web.xml中配置分散式session過濾器,初始化bean name是springSession;將需要用到分散式session請求配置至過濾器中,程式碼如下:
    <filter-mapping>    
     <filter-name>springSessionFilter</filter-name>   
     <url-pattern>/shoppingcart/*</url-pattern>
    </filter-mapping>
    <filter-mapping>    
     <filter-name>springSessionFilter</filter-name>    
     <url-pattern>/my/*</url-pattern>
    </filter-mapping>
  3. redis session配置
    配置分散式session管理bean(SessionRepositoryFilter.java),經過第二步過濾器的請求,都會經過這個管理bean,配置SessionRepositoryFilter.java預設建構函式是redis session操作倉RedisOperationsSessionRepository.java。

    <bean id="springSession" class="com.bbkmobile.iqoo.vivoshop.common.session.SessionRepositoryFilter">    
    <constructor-arg>       
    <bean class="org.springframework.session.data.redis.RedisOperationsSessionRepository">            
    <constructor-arg>               
      <bean class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"    
       p:port="${redis.port.session}"p:hostName="${redis.host.session}" 
       p:password="${redis.password.session}"       p:poolConfigref="jedisPoolConfig" />            
    </constructor-arg>        
    </bean>   
    </constructor-arg>
    </bean>

    redis如何實現session分散式存取在下一節介紹。

    分散式session請求流程

    上一節介紹了spring mvc框架下redis session系統配置管理,下面我將會redis中session請求流程進行介紹。
    如上一節所述,經過過濾器的請求,進入SessionRepositoryFilter,其預設使用RedisOperationsSessionRepository作為建構函式引數。


預設使用RedisOperationsSessionRepository作為建構函式引數


進入分散式session過濾器,SessionRepositoryFilter執行doFilterInternal方法,類似Filter中的doFilter(),在單個請求執行緒中只調用一個請求。doFilterInternal()執行doFilter操作後,再執行session commit操作wrappedRequest.commitSession();


commitSession.png


commitSession方法中,sessionRepository.save(session)呼叫RedisOperationsSessionRepository的save()方法,將session以key value形式存放到redis中。
獲取session時,SessionRepositoryFilter呼叫getSession()方法,方法內部S session = sessionRepository.getSession(requestedSessionId);呼叫RedisOperationsSessionRepository的getSession()方法,從redis中獲取session。具體RedisOperationsSessionRepository如果將session存取值redis,不再講述,可以查閱原始碼。

尊重版權:

參考文件:Tomcat中session的管理機制
博主知識短淺,如有錯誤疏漏,請各位簡客批評指正。