说白了就是服务器集群Session共享的问题,集群情况下,session保存在各自的服务器的tomcat中,当分发地址至不同服务时,导致sesson取不到,就会产生session共享问题。
Session 是客户端与服务器通讯会话跟踪技术,服务器与客户端保持整个通讯的会话基本信息。
客户端在第一次访问服务端的时候,服务端会响应一个sessionId并且将它存入到本地cookie中,在之后的访问会将cookie中的sessionId放入到请求头中去访问服务器,如果通过这个sessionid没有找到对应的数据那么服务器会创建一个新的sessionid并且响应给客户端。
假设第一次访问服务A生成一个sessionid并且存入cookie中,第二次却访问服务B客户端会在cookie中读取sessionid加入到请求头中,如果在服务B通过sessionid没有找到对应的数据那么它创建一个新的并且将sessionid返回给客户端,这样并不能共享我们的Session无法达到我们想要的目的。
解决方案:
使用cookie来完成(很明显这种不安全的操作并不可靠)
使用Nginx中的ip绑定策略,同一个ip只能在指定的同一个机器访问(不支持负载均衡)
利用数据库同步session(效率不高,访问压力大)
使用tomcat内置的session同步(同步可能会产生延迟,集群过多tomcat,session全局复制导致性能下架)
使用token代替session
使用spring-session以及集成好的解决方案,存放在redis中(读写效率高,并可在集群环境下做高可用)
启动两个SpringBoot项目端口号分别为 8080、8090 进行测试。
1、项目依赖(pom.xml)
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.3.1.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.mscloudmesh</groupId> <artifactId>springboot-redis</artifactId> <version>0.0.1-SNAPSHOT</version> <name>springboot-redis</name> <description>springboot-redis</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency> <!-- 实现session共享 --> <dependency> <groupId>org.springframework.session</groupId> <artifactId>spring-session-data-redis</artifactId> </dependency> <!-- 实现session共享--> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>2. application.yml配置文件:
server: port: 8080 spring: application: name: springboot-redis redis: host: 127.0.0.1 port: 6379 database: 0 password: jedis: pool: max-active: 200 max-wait: -1 max-idle: 10 min-idle: 0 timeout: 1000 session: store-type: redispackage com.mscloudmesh.session; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import javax.servlet.http.HttpSession; @RestController @RequestMapping("/test") public class TestController { @Value("${server.port}") private Integer port; @GetMapping("/createSession") public String createSession(HttpSession session, String name) { session.setAttribute("name", name); return "当前项目端口:" + port + " 当前sessionId:" + session.getId() + " 在Session中存入成功!"; } @GetMapping("/getSession") public String getSession(HttpSession session) { return "当前项目端口:" + port + " 当前sessionId:" + session.getId() + " 获取的姓名:" + session.getAttribute("name"); } }
3. 启动类 在 Spring boot 的文档中,添加 @EnableRedisHttpSession 来开启 `spring session`支持,配置如下:
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;//开启session共享 @EnableRedisHttpSession(maxInactiveIntervalInSeconds = 30 * 60 * 1000) @SpringBootApplication public class RedisApplication { public static void main(String[] args) { SpringApplication.run(RedisApplication.class, args); } }下面进行下测试 为了方便测试,我们修改server.port为8080,8081,运行两个应用程序 1. 启动之后进行访问测试,首先访问 8080 端口的 程序 http://localhost:8080/createSession?name=kevin 2.修改端口为8081,启动程序并访问测试 http://localhost:8080/getSession
这样两个服务器就实现session共享
3.可以通过redis客户端查看