Http协议本身是无状态的,为了保存会话信息,浏览器Cookie通过SessionID标识会话请求,服务器以SessionID为key来存储会话信息。在单实例应用中,可以考虑应用进程自身存储,随着应用体量的增长,需要横向扩容,多实例Session共享问题随之而来。
下面假设我们使用Nginx来实现负载均衡横向扩节点:
在这样的架构中,会出现无法获取到Session信息,不知道是哪个用户的请求。例如客户端发起一个请求,这个请求到了Nginx之后,会被Nginx转发到 Tomcat A上,然后在Tomcat A上的Session存储数据。后面又来一个请求,这个请求被Nginx转发到Tomcat B上,此时再去获取Session会发现没有数据。对于这样的问题有一个主流的解决办法,就是实现共享Session,把Session存储到Reids里面。如下:
这样无论存储还是读取Session的操作,都是去操作Redis中存储Session信息而不是自身内存中的Session,其他Tomcat也是如此,这样就实现了Session 共享。
一、添加依赖
除了Redis依赖之外,这里还要提供spring-session-data-redis依赖,SpringSession可以做到透明化地替换掉应用的Session容器。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<exclusions>
<exclusion>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
二、Application.properties中配置Redis连接信息:
#### redis 配置 ####
# 基本连接信息配置
spring.redis.database = 0
spring.redis.host = 127.0.0.1
spring.redis.port = 6379
spring.redis.password = 123456
# 连接池信息配置
spring.redis.jedis.pool.max-active = 8
spring.redis.jedis.pool.max-idle = 8
spring.redis.jedis.pool.max-wait = -1ms
spring.redis.jedis.pool.min-idle = 0
spring.redis.timeout = 0
三、编写测试Controller
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpSession;
@RestController
public class SessionController {
@Value("${server.port}")
String port;
@GetMapping("/save/{name}")
public String saveName(@PathVariable("name") String name, HttpSession session) {
session.setAttribute("name", name);
return port;
}
@GetMapping("/get")
public String getName(HttpSession session) {
return port + ":" + session.getAttribute("name").toString();
}
}
四、运行项目
(1)使用maven打包运行,然后传入不同端口参数启动。
mvn clean package
nohup java -jar test-0.0.1-SNAPSHOT.jar --server.port=8080 &
nohup java -jar test-0.0.1-SNAPSHOT.jar --server.port=8081 &
nohup 表示不挂断程序运行,即当终端窗口关闭后,程序依然在后台运行,最后的 & 表示让程序在后台运行。
(2)使用idea编译器启动多个项目,设置不同端口参数。
如果想使用该方案,可以查看这篇文章:IDEA 启动多个SpringBoot项目不同端口
五、验证结果
1.我们先请求8081端口的save接口,存储一个session数据。
2.请求8081端口get接口,获取session数据。
3.请求8082端口的get接口,获取session数据。