在传统的单服务架构中,只有一个服务器,那就不会存在session共享的问题,但如果在分布式/集群项目中,session共享则是一个必须面对的问题。
这样就会出现一个问题,比如说,当客户端发起了一个请求,这个请求到达Nginx之后,被转发到了服务器A,然后在服务器A上往session保存了一份数据,下次又来一个请求,这个请求被转发到 Tomcat B 上,此时再去 Session 中获取数据,发现没有之前的数据。对于这一类问题的解决,思路很简单,就是将各个服务之间需要共享的数据,保存到一个公共的地方(主流方案就是 Redis):
当所有 Tomcat 需要往 Session 中写数据时,都往 Redis 中写,当所有 Tomcat 需要读数据时,都从 Redis 中读。这样,不同的服务就可以使用相同的 Session 数据了。
这样的方案,可以由开发者手动实现,即手动往 Redis 中存储数据,手动从 Redis 中读取数据,相当于使用一些 Redis 客户端工具来实现这样的功能,毫无疑问,手动实现工作量还是蛮大的。
一个简化的方案就是使用 Spring Session 来实现这一功能,Spring Session 就是使用 Spring 中的代理过滤器,将所有的 Session 操作拦截下来,自动的将数据 同步到 Redis 中,或者自动的从 Redis 中读取数据。
对于开发者来说,所有关于 Session 同步的操作都是透明的,开发者使用 Spring Session,一旦配置完成后,具体的用法就像使用一个普通的 Session 一样。
演示
创建一个springboot项目
依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--用于连接redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--用于使用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-test</artifactId>
<scope>test</scope>
</dependency>
<!--工具类,非必需-->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>4.1.0</version>
</dependency>
application.yml
server:
port: 8088
spring:
redis:
host: localhost
port: 6379
IndexController
@RestController
@RequestMapping(value = "/")
public class IndexController {
@Value("${server.port}")
private String port;
private String username = "wangxianlin";
private String password = "123456";
private String nickname = "Halo";
@RequestMapping(value = "/login")
public Map<String, Object> getSession(HttpServletRequest request,@RequestParam("user") String user,@RequestParam("pwd") String pwd) {
Map<String, Object> map = new HashMap<>();
if (username.equals(user) && password.equals(pwd)){
request.getSession().setAttribute("user", "当前登录用户是:"+nickname);
map.put("msg", "登录成功");
map.put("port",port);
}else {
map.put("msg", "账号或密码错误");
}
return map;
}
@RequestMapping(value = "/get")
public Map<String, Object> get(HttpServletRequest request) {
Map<String, Object> map = new HashMap<>();
String user = (String) request.getSession().getAttribute("user");
map.put("msg", StrUtil.isEmpty(user)?"用户未登录":user);
map.put("port",port);
return map;
}
}
在启动类上加上注解
@EnableRedisHttpSession
将项目打成jar包
启动项目
前提:redis 是否已经开启?
将jar 分别以 8088和8089端口进行启动
java -jar --server.port=8088
java -jar --server.port=8089
配置nginx
upstream web-server {
server localhost:8088;
server localhost:8089;
}
server {
listen 9000;
server_name localhost;
location / {
proxy_pass http://web-server;
}
}
启动nginx
由于我这里有三个nginx配置文件,然后我想启动nginx-origanal.conf 咋办呢?
命令 人家早就给我们安排好了,我们只需要这样。
测试
未登录状态下
http://localhost:9000/get
登录:
http://localhost:9000/login?user=wangxianlin&pwd=123456
http://localhost:9000/get