本章概要
- Session 共享
6.3 Session 共享
正常情况下,HttpSession 是通过 Servlet 容器创建进行管理的,创建成功之后都是保存在内存中。如果开发者需要对项目进行横向扩展搭建集群,那么可以利用一些硬件或者软件工具来做负载均衡,此时,来自同一用户的 HTTP 请求就有可能被分发都不同的实例上去,如何保证各个实例之间的 Session 的同步就成为一个必须要解决的问题。
Spring Boot 提供了自动化的 Session 共享配置,它结合 Redis 可以非常方便地解决这个问题。使用 Redis 解决 Session 共享的原理非常简单,就是把原本存储在不同服务器上的 Session 拿出来放在一个独立的服务器上,如图(画的不好,凑合看)。
当一个请求到达 Nginx 服务器后,首先进行请求分发,假设请求分到 A 了,A 在 处理请求时,无论是存储 Session 还是读取 Session ,都去操作 Session 服务器而不是操作自身的内存中的 Session ,其它应用服务器在处理请求时也是如此,这样就实现 Session 共享了。
6.3.1 Session 共享配置
Spring Boot 中的 Session 共享配置非常容易,创建Spring Boot Web 项目,添加 Redis 和 Session 依赖,如下:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<exclusions>
<exclusion>
<groupId>io.lettue</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>
除了 Redis 依赖之外,这里还要提供 spring-session-data-redis 依赖,Spring Session可以做到透明化地替换掉应用的 Session 容器。项目创建成功后,在 application.properties 中进行 Redis 基本连接信息配置
spring.redis.database=0
spring.redis.host=ip地址
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
然后创建一个 Controller 用来执行测试操作
@RestController
public class HelloController {
@Value("${server.port}")
String port;
@PostMapping("/save")
public String saveName(String name, HttpSession session) {
session.setAttribute("name", name);
return port;
}
@GetMapping("/get")
public String getName(HttpSession session) {
return port + ":"
+ session.getAttribute("name").toString();
}
}
这里提供了两个方法,一个 save 接口用来向 Session 中存储数据,还有一个 get 接口用来从 Session 中获取数据,这里注入了项目启动的端口号 server.port ,主要是为了区分到底是哪个服务器提供的服务。另外,虽然还是操作的 HttpSession,但是实际上 HttpSession 容器已经被透明替换,真正的 Session 此时存储在 Redis 服务器上。
项目创建完成后,将项目打成 jar 包上传到 CentOS 上。然后执行如下两条命令启动项目:
nohup java -jar a.jar --server.port=8080 &
nohup java -jar a.jar --server.port=8081 &
nohup 表示不挂断程序运行,即当终端窗口关闭后,程序依然在后台运行,最后的 & 表示让程序在后台运行。 – server.port 指定启动的端口号。启动成功后开始配置负载均衡。
6.3.2 Nginx 负载均衡
先在 CentOS 上安装 Nginx ,下载源码并解压:
wget https://nginx.org/download/nginx-1.14.0.tar.gz
tar -zxvf /opt/package/nginx-1.14.0.tar.gz -C /opt/soft/
然后进入目录,编译安装
cd /opt/soft/nginx-1.14.0
./configure
make
make install
安装成功后,找到 Nginx 安装目录,执行 sbin 目录下的 nginx 文件启动ngxin
/usr/local/nginx/sbin/nginx
Nginx 启动成功后,默认端口是 80 ,可以在物理机直接访问
然后修改 Nginx 的配置文件
vim /usr/local/nginx/conf/nginx.conf
编辑内容如下
worker_processes 1;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
upstream myserver {
server ip地址:8080 weight=1;
server ip地址:8081 weight=1;
}
server {
listen 80;
server_name 127.0.0.1 localhost ip地址;
location / {
proxy_pass http://myserver;
root html;
index index.html index.htm;
}
}
}
然后重启 Nginx
/usr/local/nginx/sbin/nginx -s reload
6.3.3 请求分发
当 real server 和 ngxin 都启动后,调用 “/save” 接口存储数据
调用的端口是 80 ,即调用的是 Nginx 服务器,请求会被 Nginx 转发到 real server 上进行处理,返回值为 8080 ,说明真正处理请求的 real server 是 8080 那台服务器,接下来调用 get 接口获取数据
调用的端口依然是 80 ,但是返回值是 8081 ,说明是 8081 那台 real server 提供的服务,如果者里不是 8081 ,再访问一次即可。
经过如上步骤,就完成了利用 Redis 实现 Session 共享功能,基本上不需要额外配置,开箱即用。