概述
在tomcat等web容器中,session是保存在本机内存中。如果我们对tomcat做集群,不可避免要涉及到session同步的问题,必须保证同一个集群中的tomcat的session是共享的。
为了保证tomcat集群正常工作 ,通常采用以下方法:
在tomcat前端配置nginx中,在server.xml中配置ip_hash负载均衡算法来保证同一个ip的访客固定访问同一个后端服务器,避免多个tomcat需要session同步问题,缺点就是如果服务器宕机了,则所有在该服务器上的用户状态就都会丢失
通过tomcat自带的cluster方式,多个tomcat之间实时共享session信息,但是此方法随着tomcat数量和请求量增加性能会下降的比较厉害
利用filter方法
利用terracotta服务器共享session
利用redis、memcached等中间件存储session
SpringBoot中使用Redis实现session共享
引入依赖:
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
application.yaml中的配置
spring:
session:
# session的存储方式的类型配置
store-type: redis
# session 存活时间
timeout: 300
#配置Tomcat多端口号
additionalPorts: 9090,9091
TomcatConfig配置类
import org.apache.catalina.connector.Connector;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.ArrayList;
import java.util.List;
/**
* @Description: 增加端口监听
* @Class: TomcatConfig
* @Author: Yiang37
* @Date: 2021/9/3 15:56
* @Version: 1.0
*/
@Configuration
public class TomcatConfig {
@Value("${server.additionalPorts}")
private String additionalPorts;
@Bean
public TomcatServletWebServerFactory servletContainer() {
TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory();
Connector[] additionalConnectors = this.additionalConnector();
if (additionalConnectors != null && additionalConnectors.length > 0) {
tomcat.addAdditionalTomcatConnectors(additionalConnectors);
}
return tomcat;
}
private Connector[] additionalConnector() {
if (StringUtils.isBlank(this.additionalPorts)) {
return null;
}
// 端口按,分割
String[] ports = this.additionalPorts.split(",");
List<Connector> result = new ArrayList<>();
for (String port : ports) {
Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
connector.setScheme("http");
connector.setPort(Integer.parseInt(port));
result.add(connector);
}
return result.toArray(new Connector[] {});
}
}
启动类上添加注解
@import({TomcatConfig.class})
测试类RedisHandler
此类中有三个方法:
login()模拟登录,在session中存储一个值
@RequestMapping("/login")
publicMap<String,Object>login(HttpServletRequestrequest){
HttpSessionsession=request.getSession();
//设置session中的值
session.setAttribute("username","sq"+System.currentTimeMillis());
Map<String, Object>sqMap=newHashMap<>();
Enumeration<String>attributeNames=request.getSession().getAttributeNames();
while (attributeNames.hasMoreElements()){
Stringname=attributeNames.nextElement();
sqMap.put(name,session.getAttribute(name));
}
sqMap.put("sessionId",session.getId());
returnsqMap;
}
getSession():从session中获取值
@RequestMapping("get-session")
publicObjectgetSession(HttpServletRequestrequest) {
HttpSessionhttpSession=request.getSession();
Map<String,Object>rtnMap=newHashMap<>();
Enumeration<String>attributeNames=request.getSession().getAttributeNames();
while(attributeNames.hasMoreElements()){
Stringname=attributeNames.nextElement();
rtnMap.put(name, httpSession.getAttribute(name));
}
intcount;
try {
count=Integer.parseInt(String.valueOf(httpSession.getAttribute("count")));
count++;
}catch (NumberFormatExceptione){
count=1;
}
httpSession.setAttribute("count",count+"");
rtnMap.put("sessionId", httpSession.getId());
returnrtnMap;
}
invalidate(): 使的sesion值失效
@RequestMapping("invalidate")
publicintinvalidate(HttpServletRequestrequest) {
HttpSessionhttpSession=request.getSession();
httpSession.invalidate();
return1;
}
测试
启动RedisApplication应用,再启动项目
登录:localhost:8080/login
![](https://i-blog.csdnimg.cn/blog_migrate/e7de406e66f2336d132559731bda3382.png)
获取session
![](https://i-blog.csdnimg.cn/blog_migrate/49cf8ac7ca79438b3b4fedf2007c5925.png)
从另一个服务器端口号访问
![](https://i-blog.csdnimg.cn/blog_migrate/1ff3ba6f663a49c7e5e32edca0ae93bf.png)
服务重启后sessionid不变,说明redis保存session生效,成功!