概述
session的基础知识就不再多说。
通常,我们会把一个项目部署到多个tomcat上,通过Nginx进行负载均衡,提高系统的并发性。此时,就会存在一个问题。假如用户第一次访问tomcat1,并登陆保存了用户信息,但是下一次访问的时候,Nginx让用户访问tomcat2,此时tomcat2中并没有用户的session信息,用户必须重新进行登录操作。这样会极大的破坏用户的体验。
对此,我们有两大类解决方案。一个是将Nginx的负载均衡机制设为根据iphash,也就是用户每次保证能访问同一台tomcat,但是该方法也存在着弊端,当tomcat挂掉以后,用户必须重新登陆。第二种方法较为常用,就是通过某些方法进行session共享。常用的方法有使用spring session,如果项目中有用到shiro,可以通过重写AbstractSessionDAO,即重写shiro的session存储机制完成。
本文将先从spring session入手,完成session共享。由于最近接触的是一个老项目,使用ssm框架,本文基于xml的方式进行配置。熟悉spring boot的朋友可以直接到spring boot官网进行查看,配置较为简单。由于老项目的并发量并不是很高,因此本文使用spirng-session-jdbc来进行session共享。大型项目可选择使用非关系型数据库Redis等
代码地址
https://github.com/DenchZhou/ssm/tree/master/spring-session-jdbc
如果有用的话不妨给个star,感恩!
配置
Maven依赖
加入maven依赖
<dependency> <groupId>org.springframework.session</groupId> <artifactId>spring-session-jdbc</artifactId> <version>2.0.2.RELEASE</version> </dependency>
数据库表
我们需要添加两张数据库表,spring session通过将httpsession序列化保存至数据库,需要session的时候再从数据库中取出并反序列化。因此我们需要保证数据库中存在保存session的表格。
CREATE TABLE SPRING_SESSION ( PRIMARY_ID CHAR(36) NOT NULL,SESSION_ID CHAR(36) NOT NULL,CREATION_TIME BIGINT NOT NULL,LAST_ACCESS_TIME BIGINT NOT NULL,MAX_INACTIVE_INTERVAL INT NOT NULL,EXPIRY_TIME BIGINT NOT NULL,PRINCIPAL_NAME VARCHAR(100),CONSTRAINT SPRING_SESSION_PK PRIMARY KEY (PRIMARY_ID) ) ENGINE=InnoDB ROW_FORMAT=DYNAMIC; CREATE UNIQUE INDEX SPRING_SESSION_IX1 ON SPRING_SESSION (SESSION_ID); CREATE INDEX SPRING_SESSION_IX2 ON SPRING_SESSION (EXPIRY_TIME); CREATE INDEX SPRING_SESSION_IX3 ON SPRING_SESSION (PRINCIPAL_NAME); CREATE TABLE SPRING_SESSION_ATTRIBUTES ( SESSION_PRIMARY_ID CHAR(36) NOT NULL,ATTRIBUTE_NAME VARCHAR(200) NOT NULL,ATTRIBUTE_BYTES BLOB NOT NULL,CONSTRAINT SPRING_SESSION_ATTRIBUTES_PK PRIMARY KEY (SESSION_PRIMARY_ID,ATTRIBUTE_NAME),CONSTRAINT SPRING_SESSION_ATTRIBUTES_FK FOREIGN KEY (SESSION_PRIMARY_ID) REFERENCES SPRING_SESSION(PRIMARY_ID) ON DELETE CASCADE ) ENGINE=InnoDB ROW_FORMAT=DYNAMIC;
spring xml相关配置
<context:annotation-config/> <bean class="org.springframework.session.jdbc.config.annotation.web.http.JdbcHttpSessionConfiguration"> </bean> <!-- 配置事务管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <!-- 注入数据库连接池 --> <property name="dataSource" ref="dataSource" /> </bean>
使用context:annotation-config和JdbcHttpSessionConfiguration是因为Spring Session没有提供XML命名空间的支持。这就创建了一个实现了Filter的名为springSessionRepositoryFilter 的SpringBean。此过滤器负责使用Spring Session支持的一个实现去替换HttpSession。
然后就是事务管理器的配置,数据源的配置不再详细说明了。可以看最上面的项目源码。
web.xml配置过滤器
spring session通过自定义一个filter,通过filter职责链将用自己定义的request替换httpservletrequest,从而使用自己httpsession。因此我们必须配置一下Filter,并且最好把他放在最前面,使其优先执行。
<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>
spring 配置读取我们的spring配置文件。
<servlet> <servlet-name>dispatcher</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring-*.xml</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>dispatcher</servlet-name> <!-- 默认匹配所有的请求 --> <url-pattern>/</url-pattern> </servlet-mapping>
测试
我的项目中只写了一个简单的查找user并放入session中
@Controller public class UserController { @Autowired private UserService userService; @RequestMapping("/findUser") @ResponseBody public String findUserById(HttpServletRequest request){ UserEntity user = userService.findUserById(1); HttpSession session = request.getSession(); session.setAttribute("user",user); return user.toString(); } }
可以通过数据库管理工具查看