消息模式
因?yàn)閔ttp是本身是一個(gè)無(wú)狀態(tài)協(xié)議篇梭。為了記錄用戶的狀態(tài)弓坞,session機(jī)制就應(yīng)運(yùn)而生了,servlet的標(biāo)準(zhǔn)本身是包含session的蚤氏,Tomcat會(huì)把session的信息存儲(chǔ)在服務(wù)器內(nèi)存里代赁,Request提供了獲取session的方法扰她。然而這種session數(shù)據(jù)沒(méi)有一定的持久化機(jī)制,而且難以實(shí)現(xiàn)應(yīng)用服務(wù)器的水平擴(kuò)展芭碍。在負(fù)載均衡器 + 應(yīng)用服務(wù)器集群的架構(gòu)中徒役,session共享是一個(gè)基本的要求。Spring Session支持把session信息以各種形式存儲(chǔ)窖壕,Spring Session支持把session信息以各種形式存儲(chǔ)忧勿,比如數(shù)據(jù)庫(kù)或者Redis。項(xiàng)目上一般都是將Session存放在Redis中瞻讽,session數(shù)據(jù)都是有時(shí)效性的鸳吸,Redis的數(shù)據(jù)超時(shí)機(jī)制可以很好的完成session信息的清除;此外速勇,Redis的數(shù)據(jù)訪問(wèn)速度更快晌砾,對(duì)于時(shí)效性較強(qiáng)的session數(shù)據(jù),會(huì)有比較好的加速效果烦磁。
spring redis
使用
在web.xml文件中添加一下過(guò)濾器养匈。
<filter>
<filter-name>spring-session</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<async-supported>true</async-supported>
<init-param>
<param-name>targetBeanName</param-name>
<param-value>springSession</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>spring-session</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
注意這里的
<init-param>
<param-name>targetBeanName</param-name>
<param-value>springSession</param-value>
</init-param>
這里配置的這個(gè)targetBeanName在下面的配置文件中是有用到的哼勇!如果未指定init-param參數(shù)的話,DelegatingFilterProxy就會(huì)把filter-name作為要查找的Bean對(duì)象呕乎,這也是DelegatingFilterProxy類的作用猴蹂。如果配置了,就會(huì)去找 ID 為 "springSession" 的Bean 作為 Spring-Session的 過(guò)濾器楣嘁。 可以看出每一個(gè)請(qǐng)求都會(huì)經(jīng)過(guò)該filter,經(jīng)過(guò)該filter的請(qǐng)求也會(huì)相應(yīng)的經(jīng)過(guò)springSessionRepositoryFilter這個(gè)過(guò)濾器珍逸。
redis.useSentinel=false
redis.ip=localhost
redis.port=6379
redis.db=2
redis.password=
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<!-- Root Context: defines shared resources visible to all other web components -->
<!-- 引入config.properties屬性文件 -->
<context:property-placeholder location="classpath:config.properties"/>
<bean id="objectMapper" class="com.fasterxml.jackson.databind.ObjectMapper">
</bean>
<bean id="redisSentinelConfiguration"
class="org.springframework.data.redis.connection.RedisSentinelConfiguration">
<property name="master">
<bean class="org.springframework.data.redis.connection.RedisNode">
<property name="name" value="mymaster"/>
</bean>
</property>
<property name="sentinels" ref="redisNodes"/>
</bean>
<bean id="redisNodes" class="com.hand.hap.core.impl.RedisNodeAutoConfig">
<property name="sentinels" value="${redis.sentinel}"/>
</bean>
<!--這里添加的是Redis逐虚,因?yàn)槭褂玫氖荢pring里自帶的Redis的Session策略 -->
<bean id="v2redisConnectionFactory" class="com.hand.hap.core.JedisConnectionFactoryBean">
<property name="useSentinel" value="${redis.useSentinel}"/>
<property name="sentinelConfiguration" ref="redisSentinelConfiguration"/>
<property name="hostName" value="${redis.ip:localhost}"/>
<property name="port" value="${redis.port:6379}"/>
<property name="database" value="${redis.db:10}"/>
<property name="password" value="${redis.password:}"/>
<property name="poolConfig">
<bean class="redis.clients.jedis.JedisPoolConfig">
<property name="maxIdle" value="999"/>
<property name="maxTotal" value="9999"/>
<property name="minIdle" value="20"/>
</bean>
</property>
</bean>
<bean id="stringRedisSerializer"
class="org.springframework.data.redis.serializer.StringRedisSerializer"/>
<bean id="v2redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
<property name="connectionFactory" ref="v2redisConnectionFactory"/>
<property name="keySerializer" ref="stringRedisSerializer"/>
<property name="valueSerializer" ref="stringRedisSerializer"/>
<property name="hashKeySerializer" ref="stringRedisSerializer"/>
<property name="hashValueSerializer" ref="stringRedisSerializer"/>
</bean>
<!-- 這里的是為了下面的 Session策略過(guò)濾器提供構(gòu)造函數(shù)傳入的參數(shù),因?yàn)镾ession過(guò)濾器要依賴該對(duì)象來(lái)構(gòu)造谆膳,所以創(chuàng)建一個(gè)先 -->
<bean name="redisOperationsSessionRepository"
class="org.springframework.session.data.redis.RedisOperationsSessionRepository">
<constructor-arg ref="v2redisConnectionFactory"/>
<property name="defaultMaxInactiveInterval" value="${session.expire.time:3600}"/>
</bean>
<!-- 這個(gè)是Session策略過(guò)濾器叭爱,即將容器原有的Session持久化機(jī)制,代替為Spring的 Redis持久化Session機(jī)制漱病。 -->
<!-- 注意买雾,這個(gè)名字與 web.xml里的targetBean的下value是要一致的。 -->
<bean name="springSession" class="org.springframework.session.web.http.SessionRepositoryFilter">
<constructor-arg ref="redisOperationsSessionRepository"/>
<property name="httpSessionStrategy" ref="cookieHttpSessionStrategy"/>
</bean>
</beans>
大概就是 SessionRepositoryFilter 攔截所有請(qǐng)求杨帽,springSessionRepositoryFilter替換容器(Tomcat)默認(rèn)的HttpSession支持為Spring Session漓穿,將Session實(shí)例存放在Redis中∽⒂可以看看它的關(guān)鍵代碼
@Order(SessionRepositoryFilter.DEFAULT_ORDER)
public class SessionRepositoryFilter<S extends ExpiringSession> extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request,HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
request.setAttribute(SESSION_REPOSITORY_ATTR, this.sessionRepository);
SessionRepositoryRequestWrapper wrappedRequest = new SessionRepositoryRequestWrapper(
request, response, this.servletContext);
SessionRepositoryResponseWrapper wrappedResponse = new SessionRepositoryResponseWrapper(
wrappedRequest, response);
HttpServletRequest strategyRequest = this.httpSessionStrategy
.wrapRequest(wrappedRequest, wrappedResponse);
HttpServletResponse strategyResponse = this.httpSessionStrategy
.wrapResponse(wrappedRequest, wrappedResponse);
try {
filterChain.doFilter(strategyRequest, strategyResponse);
}
finally {
wrappedRequest.commitSession();
}
}
......
}
- SessionRepositoryRequestWrapper是HttpServletRequest的子類晃危,接管原來(lái)的request,重寫(xiě)了與session相關(guān)的方法老客。
- SessionRepositoryResponseWrapper是HttpServletResponse的子類僚饭,接管原來(lái)的response。
因?yàn)镾essionRepositoryRequestWrapper繼承了HttpServletRequestWrapper胧砰,而HttpServletRequestWrapper實(shí)現(xiàn)了HttpServletRequest接口鳍鸵,在SessionRepositoryRequestWrapper又重寫(xiě)了HttpServletRequest接口中的一些方法,所以才會(huì)有:getSession尉间、changeSessionId等這些方法偿乖。
到此,我們應(yīng)該大致明白了乌妒,原有的request請(qǐng)求和response都被重新進(jìn)行了包裝汹想。我們也就明白了原有的HttpSeesion是如何被Spring Session替換掉的。
image.png
可以通過(guò)繼承HttpServletRequestWrapper 達(dá)到修飾 HttpServletRequest 的目的撤蚊。
同理古掏,HttpServletResponseWrapper 類似。