简介
Session一直是我们做集群时一个很头疼的问题,之前有一个GitHub上开源的Tomcat-redis-session-manager
,但是它只支持到Tomcat7,所以不是一种最佳选择,所以才会有今天的Spring-session 和 redis
来做session共享 。
session共享的几种方式
- Tomcat的Session复制(在
<Engine>
节点下+Cluster
)
1、实现方式简单,但是只适合在小集群中使用,如果集群过大Session间的复制将会带来很大的流量消耗
2、 tomcat的session复制分为两种, 一种是全局试的(all-to-all), 这意味着一个node(tomcat实例)的session发生 变化之后, 它会将这些变更复制到其他所有集群组的成员;另一种是局部试的, 它会用到BackupManager, BackupManager能实现只复制给一个Buckup Node, 并且这个Node会部署相同的Web应用, 但是这种方式并没用经过很多的测试。
3、Tomcat的session复制是基于IP组播(multicast)来完成的。简单的说就是需要进行集群的tomcat通过配置统一的组播IP和端口来确定一个集群组, 当一个node的session发生变更的时候, 它会向IP组播发送变更的数据, IP组播会将数据分发给所有组里的其他成员(node)。
4、如果换了别的web服务器就没办法使用了(与web容器耦合性太强)
- 第三方:
Tomcat-redis-session-manager
而且现在官方也没去更新tomcat-redis-session-manager的jar包了(仍然停留在支持tomcat7上,一些自己修改的jar包除外)。
只支持到了Tomcat7
- 利用Nginx Session共享
Nginx中的ip_hash 技术能够将某个ip的请求负载均衡到同一台服务器上,这样一来这个IP下的某个客户端和后台就可以就能建立起固定的session。ip_hash 是在upstream配置中定义的。具体配置如下:
upstream www.test.com {
ip_hash;
server 192.168.74.131:8080;
server 192.168.74.131:8081;
}
server {
listen 80;
server_name www.test.com; ## server_name要与自定义的upstream一致。
location / {
proxy_pass http://www.test.com; ## 添加代理路径。
}
ip_hash 是通过请求端的ip地址计算hash值,与后端服务器定位。由于相同的ip计算出的hash值都是一样的,所以同一台机器的请求每次都可以落到相同的服务器上。但这种方式的局限性太大。比如,客户端请求的ip是动态的或者那台服务宕机了,对应的后端服务器出现故障。这些都使session共享变得不可用。所以使用ip_hash方式实现的session共享不是一个最佳的解决方案
Spring-session 和 redis
1.Spring Session提供了redis、jvm的map、mongo、gemfire、hazelcast、jdbc等多种存储session的容器的方式。
2.同一个浏览器同一个网站,支持多个session。
3.不依赖于cookie。可通过header来传递sessionID
4.WebSocket和spring-session结合,同步生命周期管理。
5.使用简单
6.不依赖与具体的web容器
web.xml 中 spring session的配置
这里使用了Spring Web
提供的代理过滤器,将拦截到的请求全部交给一个名为springSessionRepositoryFilter
的过滤器进行处理(注意:这个过滤器要配置在第一个)
<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>
applicationContext.xml中配置Spring Session和Redis,如下:
hostName:redis的地址,port 为redis的服务端口号
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxIdle" value="10"/><!-- 最大空闲连接数, 默认8个 -->
<property name="maxTotal" value="20"/><!-- 最大连接数, 默认8个 -->
<property name="blockWhenExhausted" value="true"/><!-- 连接耗尽时是否阻塞, false报异常,ture阻塞直到超时, 默认true -->
<property name="maxWaitMillis" value="1000"/><!-- 获取连接时的最大等待毫秒数(如果设置为阻塞时BlockWhenExhausted),如果超时就抛异常, 小于零:阻塞不确定的时间, 默认-1 -->
<property name="testOnBorrow" value="true"/><!-- 在获取连接的时候检查有效性, 默认false -->
</bean>
<!-- redis连接配置,依次为主机ip,端口,密码,是否使用池,连接池配置引用 -->
<bean id="connectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"
p:host-name="192.168.17.132" p:port="6379" p:password="123456" p:usePool="true" p:pool-config-ref="jedisPoolConfig">
</bean>
<!-- 配置spring-session -->
<bean class="org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration">
<!-- session过期时间,单位是秒 -->
<property name="maxInactiveIntervalInSeconds" value="30"></property>
</bean>
</beans>
注意,用了spring session后,在web.xml设置的session过期时间是无效。因为此session已经不是原来的存储在容器缓存内的session了,而是被封装多了一层后存储在redis的session。
只需要上述两步就完成了Spring-session
的配置了,是不是很简单!
Server端示例:
@Controller
public class HelloController {
@RequestMapping("/setsession")
@ResponseBody
public void setSession(HttpSession session, String name) {
session.setAttribute("name", name);
}
@RequestMapping(value = "/getsession",produces = "text/html;charset=utf-8")
@ResponseBody
public String getSession(HttpSession session, HttpServletRequest req) {
return session.getAttribute("name").toString()+"-----"+req.getServletContext().getRealPath("/");
}
}