分布式Session的实现原理
Session的实现原理
我们先来看下单机Session的实现原理.
- 客户端第一次发送请求到服务端.
- 服务端生成Session和Cookie, Cookie中存储了JSESSIONID, 服务端将Cookie返回给客户端.
- 客户端再次发送请求到服务端时, 请求Cookie中会携带JSESSIONID.
- 服务端根据Cookie中的JSESSIONID找到对应的Session.
在多机部署的场景下, 如果第二次请求打到了另一台服务器, 该服务器上没有JSESSIONID对应的Session, 就会创建一个新的Session, 这样就导致Session失效了.
分布式Session的实现原理
那如何实现分布式Session呢? 其实很简单, 我们把Session中的数据存储到Redis中, 使用哈希数据类型, 格式如下:
//JSESSIONID是具体的随机串
spring:session:sessions: JSESSIONID key value
然后使用一个过滤器过滤请求和响应, 来实现将数据从Redis中写入Session和将Session中的数据写入Redis.
Spring Session + Redis
Spring Session 使用的就是我们上面描述的实现原理, 我们来看下具体实例.
(1) Maven依赖
Spring版本为4.3.9
<!--spring session-->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.8.2</version>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
<version>1.2.2.RELEASE</version>
</dependency>
(2) web.xml
Spring Session的过滤器要配置在第一位.
<!--Spring Session-->
<filter>
<filter-name>springSessionRepositoryFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSessionRepositoryFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
(3) Spring配置
配置redis.
<!--Spring Session-->
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxTotal" value="100" />
<property name="maxIdle" value="10" />
</bean>
<bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" destroy-method="destroy">
<property name="hostName" value="172.0.0.1"/>
<property name="port" value="6379"/>
<property name="timeout" value="3000"/>
<property name="usePool" value="true"/>
<property name="poolConfig" ref="jedisPoolConfig"/>
</bean>
<bean id="redisHttpSessionConfiguration" class="org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration">
<property name="maxInactiveIntervalInSeconds" value="600"/>
</bean>
(4) 测试Controller
向session添加数据和从session中取数据.
@RequestMapping("/set.do")
@ResponseBody
public String saveLoginToken(HttpServletRequest request) {
String uuid = UUID.randomUUID().toString();
request.getSession().setAttribute("loginToken", uuid);
return "success";
}
@RequestMapping("/get.do")
@ResponseBody
public String getLoginToken(HttpServletRequest request) {
return String.valueOf(request.getSession().getAttribute("loginToken"));
}
(5) 结果
发送请求到服务器时, 会携带Session对应的JSESSIONID, 然后从Redis中获取Session数据.
localhost:8080/api/set.do
localhost:8080/api/get.do
Tomcat + Redis
在Tomcat端将Session与Redis进行数据交互, 这种办法与Tomcat严重耦合了, 不推荐使用.
Tomcat8已不支持这个配置, 在Tomcat7下进行测试, 修改conf/context.xml.
<Valve className="com.orangefunction.tomcat.redissessions.RedisSessionHandlerValve" />
<Manager className="com.orangefunction.tomcat.redissessions.RedisSessionManager"
host="{redis.host}"
port="{redis.port}"
database="{redis.dbnum}"
maxInactiveInterval="60"/>