Spring Boot中实现session共享

在传统的单服务中,一般来说就只有一个服务器,不能存在session共享问题,但是在分布式项目中,session问题就显得尤为重要了,来看看下面这个图
Spring Boot中实现session共享_第1张图片
在这样的架构中,会出现一些单服务中不存在的问题,例如客户端发起一个请求,这个请求到达 Nginx 上之后,被 Nginx 转发到 Tomcat A 上,然后在 Tomcat A 上往 session 中保存了一份数据,下次又来一个请求,这个请求被转发到 Tomcat B 上,此时再去 Session 中获取数据,发现没有之前的数据。对于这一类问题的解决,思路很简单,就是将各个服务之间需要共享的数据,保存到一个公共的地方(主流方案就是 Redis):
Spring Boot中实现session共享_第2张图片
当所有的tomcat都往session中写数据时,就统一往redis中写数据,同理所有tomcat读数据就去redis中读,这样就可以做到session共享了,不同的服务就可以使用相同的 Session 数据了,这样的方案,可以由开发者手动实现,即手动往 Redis 中存储数据,手动从 Redis 中读取数据,相当于使用一些 Redis 客户端工具来实现这样的功能,毫无疑问,手动实现工作量还是蛮大的。

一个简化的方案就是使用 Spring Session 来实现这一功能,Spring Session 就是使用 Spring 中的代理过滤器,将所有的 Session 操作拦截下来,自动的将数据 同步到 Redis 中,或者自动的从 Redis 中读取数据。

对于开发者来说,所有关于 Session 同步的操作都是透明的,开发者使用 Spring Session,一旦配置完成后,具体的用法就像使用一个普通的 Session 一样。

创建工程

创建项目时,引入web、spring session以及redis依赖
Spring Boot中实现session共享_第3张图片
我这里的spring boot版本是2.1.7,所以要远程访问redis需要添加spring-security依赖

 <dependency>
 <groupId>org.springframework.bootgroupId>
     <artifactId>spring-boot-starter-data-redisartifactId>
 dependency>
 <dependency>
     <groupId>org.springframework.bootgroupId>
     <artifactId>spring-boot-starter-webartifactId>
 dependency>
 <dependency>
     <groupId>org.springframework.sessiongroupId>
     <artifactId>spring-session-data-redisartifactId>
 dependency>

 <dependency>
     <groupId>org.springframework.bootgroupId>
     <artifactId>spring-boot-starter-testartifactId>
     <scope>testscope>
 dependency>
 <dependency>
     <groupId>org.springframework.securitygroupId>
     <artifactId>spring-security-coreartifactId>
     <version>5.1.6.RELEASEversion>
 dependency>

redis的基本配置

redis默认端口6379,默认库则是0号库,由于我的redis这里没有设置密码,所以配置如下

spring.redis.host=10.0.0.13
spring.redis.port=6379
spring.redis.database=0

创建demo

package com.zhouym.spring_session;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpSession;

/**
 * 〈〉
 *
 * @author zhouym
 * @create 2019/8/28
 * @since 1.0.0
 */
@RestController
public class HelloController {

    @Value("${server.port}")
    Integer port;
    @GetMapping("/set")
    public String set(HttpSession session){
        session.setAttribute("name","zhouym");
        return String.valueOf(port);
    }

    @GetMapping("/get")
    public String get(HttpSession session){
        return session.getAttribute("name") +":"+port;
    }
}

为了获取每一个请求到底是哪一个 Spring Boot 提供的服务,需要在每次请求时返回当前服务的端口号,因此这里我注入了 server.port 。下面我们执行打包操作
Spring Boot中实现session共享_第4张图片
打包后,在后台输入一下命令

在这里插入图片描述
再启动一个实例
在这里插入图片描述
然后先访问localhost:8080/set8080这个服务的 Session 中保存一个变量,访问完成后,数据就已经自动同步到 Redis 中 了 :
在这里插入图片描述
然后,再调用localhost:8086/get 接口,就可以获取到 8080 服务的 session 中的数据:
在这里插入图片描述
此时关于 session 共享的配置就已经全部完成了,session 共享的效果我们已经看到了,但是每次访问都是我自己手动切换服务实例,因此,接下来我们来引入 Nginx ,实现服务实例自动切换。

使用nginx

进入 Nginx 的安装目录的 conf 目录下(默认是在 /usr/local/nginx/conf),编辑 nginx.conf 文件:
Spring Boot中实现session共享_第5张图片
1、upstream 表示配置上游服务器
2、zhouym表示服务器集群的名字,这个可以随意取名字
3、upstream 里边配置的是一个个的单独服务
4、weight 表示服务的权重,意味者将有多少比例的请求从 Nginx 上转发到该服务上
5、location 中的 proxy_pass 表示请求转发的地址,/ 表示拦截到所有的请求,转发转发到刚刚配置好的服务集群中
6、proxy_redirect 表示设置当发生重定向请求时,nginx 自动修正响应头数据(默认是 Tomcat 返回重定向,此时重定向的地址是 Tomcat 的地址,我们需要将之修改使之成为 Nginx 的地址)。

配置完成后,将本地的 Spring Boot 打包好的 jar 上传到 Linux ,然后在 Linux 上分别启动两个 Spring Boot 实例:
Spring Boot中实现session共享_第6张图片

nohup java -jar sessionshare-0.0.1-SNAPSHOT.jar --server.port=8080 &
nohup java -jar sessionshare-0.0.1-SNAPSHOT.jar --server.port=8081 &

nohup 表示当终端关闭时,Spring Boot 不要停止运行
& 表示让 Spring Boot 在后台启动

之后重启nginx 执行如下命令

/usr/local/nginx/sbin/nginx -s reload

Nginx 启动成功后,我们首先手动清除 Redis 上的数据,然后访问 10.0.0.13/set 表示向 session 中保存数据,这个请求首先会到达 Nginx 上,再由 Nginx 转发给某一个 Spring Boot 实例:
Spring Boot中实现session共享_第7张图片
如上,表示端口为 8081 的 Spring Boot 处理了这个 /set 请求,再访问 /get 请求:
Spring Boot中实现session共享_第8张图片
可以看到,/get 请求是被端口为 8086 的服务所处理的。
我们再次刷新
Spring Boot中实现session共享_第9张图片
可以看到get请求被8080端口的服务处理了

你可能感兴趣的:(Spring Boot中实现session共享)