Sesssion共享问题是因为在服务器集群下,用户访问服务器将值存放到Session中,再次访问的话代理服务器又会分配一个新的服务器(无法保证这个服务还是之前访问且Session中有数据的服务器),
这种情况有很多种解决的方案,例如使用nginx的ip绑定,使用cookie,数据库,缓存等等
最好使用资源最少的情况就是使用SpringSession和Redis来解决
实现原理大致如下:
在A服务器中往Session中放入的值,会被SpringSession监听到并写入到redis中,在其他服务器从session取值的时候就会从session找到值并且返回,
往session中存值和取值都无需手动的吊用redis的API,SpringSession底层会帮助我们实现
依赖pom.xml
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.2.0.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-redis</artifactId> </dependency> <dependency> <groupId>org.springframework.session</groupId> <artifactId>spring-session-data-redis</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>2.5</version> </dependency> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>3.1.0</version> </dependency>
核心配置文件application.properties
主要是配置redis服务器的参数,和服务端口
spring.redis.host=192.168.118.3
spring.redis.port=6379
spring.redis.password=admin
#服务器端口
server.port=6060
创建一个controller
用于模拟用户存取数据
package com.ty.controller; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; @RestController public class SessionController { @Value("${server.port}") private String Port; @RequestMapping("/setSession") public Object setSession(HttpServletRequest request,String sessionKey,String sessionValue){ HttpSession httpSession=request.getSession(); httpSession.setAttribute(sessionKey,sessionValue); System.out.println("sessionKey"+sessionKey); System.out.println("sessionValue"+sessionValue); return "success,port:"+Port; } @RequestMapping("/getSession") public Object getSession(HttpServletRequest request,String sessionKey){ System.out.println("sessionKey:"+sessionKey); HttpSession httpSession=null; httpSession = request.getSession(false); String value=null; if (httpSession!=null) { value=(String) httpSession.getAttribute(sessionKey); } return "sessionValue:"+value+"---port"+Port; } }
启动端口为6060的项目,然后修改配置文件,将端口改为6061再次启动,
把下图中的勾选就可启动两个除端口不一样的两个服务了
nginx配置(可有可无)
如果不配置nginx做轮询策略的话,就往a服务器里放入一个数据,在通过b服务器访问这个session用于测试就好了
upstream backserver { server localhost:6060; server localhost:6061;} server { listen 80; server_name localhost; #charset koi8-r; #access_log logs/host.access.log main; location / { proxy_pass http://backserver; index index.html index.htm; } }
开始测试
访问Controller将值放入到session中
http://localhost/setSession?sessionKey=1&sessionValue=1516
可以看到将值放入到端接为6061的服务中了,在看看redis中的变化
redis中保存着一个数据包,包中有session,key就是Session的id
接着取值
http://localhost/getSession?sessionKey=1
由图可见访问端口6060的服务器也是将值给取出来了
这样就可以解决Session共享的问题
用nginx解决话就很简单了(不推荐)
就是在负载均衡的策略下添加ip_hash,根据客户端的ip来决定访问的是哪个服务器,
这样的话相同的ip永远就会访问到同一个服务器,也就不存在什么Session共享的问题了
server { listen 80; server_name localhost; #charset koi8-r; #access_log logs/host.access.log main; location / { ip_hash proxy_pass http://backserver; index index.html index.htm; } }