欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

SpringBoot项目之基于Cookie传输sessionId的分布式会话解决方案(三)

程序员文章站 2022-07-05 23:14:49
...

上一章节讲到【SpringBoot项目高并发性能优化之Nginx负载均衡水平扩展(二)】,我们的项目做了分布式的水平扩容,利用nginx的负载均衡,但是此时我们的系统若是有用到用户登录态的地方都会遇到问题。例如我们的下单操作,会出现一个神奇的现象:当我们登录成功,浏览商品准备下单,但是此时会突然跳出您还没有登陆的提示,需要重新登陆,于是又重新登陆一次。重复操作又是提示登陆,根本无法下单。可能偶尔性的能操作成功一次。

追根揭底,还是因为我们后台项目做了负载均衡的管理机制。我们每一次登陆的sessionId都只是会存在自己的tomcat容器一份,所以多台的话根本无法共享session。

解决方案其实也很简单,就是引入我们的redis,让他做一个集中的sessionId存储管理,这样我们后台不管有几台服务器,只要是登陆成功之后都会共享这次登陆成功之后的sessionID了。好,废话不多说,直接看如何实现

1)首先看一下我们用户登陆的实现代码,比较简单,如下:

 //用户登陆接口
    @RequestMapping(value = "/login",method = {RequestMethod.POST},consumes={CONTENT_TYPE_FORMED})
    @ResponseBody
    public CommonReturnType login(@RequestParam(name="telphone")String telphone,
                                  @RequestParam(name="password")String password) throws BusinessException, UnsupportedEncodingException, NoSuchAlgorithmException {
        //入参校验
        if(org.apache.commons.lang3.StringUtils.isEmpty(telphone)||
                StringUtils.isEmpty(password)){
            throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR);
        }
        //用户登陆服务,用来校验用户登陆是否合法
        UserModel userModel = userService.validateLogin(telphone,this.EncodeByMd5(password));
        //将登陆凭证加入到用户登陆成功的session内
        this.httpServletRequest.getSession().setAttribute("IS_LOGIN",true);
        this.httpServletRequest.getSession().setAttribute("LOGIN_USER",userModel);

        return CommonReturnType.create(null);
    }

主要是看下面这两句代码:

//将登陆凭证加入到用户登陆成功的session内
 this.httpServletRequest.getSession().setAttribute("IS_LOGIN",true);
 this.httpServletRequest.getSession().setAttribute("LOGIN_USER",userModel);


我们这里直接使用了springboot内嵌tomcat的httpServletRequest中封装的session机制实现的

然后当我们下单的时候需要获取session信息,部分代码如下:

        //获取登录态的标识
        Boolean isLogin = (Boolean) httpServletRequest.getSession().getAttribute("IS_LOGIN");
        if(isLogin == null || !isLogin.booleanValue()){
            throw new BusinessException(EmBusinessError.USER_NOT_LOGIN,"用户还未登陆,不能下单");
        }
        //获取用户的登陆信息
        UserModel userModel = (UserModel)httpServletRequest.getSession().getAttribute("LOGIN_USER");
        //下单操作
        OrderModel orderModel = orderService.createOrder(userModel.getId(),itemId,promoId,amount);

2)好,看了以上的简单实现,我们接着集成redis,引入配置

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.session</groupId>
      <artifactId>spring-session-data-redis</artifactId>
    </dependency>
    <!-- https://mvnrepository.com/artifact/redis.clients/jedis -->
    <dependency>
      <groupId>redis.clients</groupId>
      <artifactId>jedis</artifactId>
      <version>2.9.0</version>
    </dependency>

3)新建一个配置,这个配置用来做我们redis的定制化的开发,例如自定义设置session的过期时间等等,这里我们先不做具体实现,所以先空着:

@Component
@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 3600)
public class RedisConfig {
}

4)配置文件我们需要修改,但是因为我们之前讲解一直在使用外挂配置文件的方式,所以这一块的配置得存放到两个服务器项目的【application.properties】中。下面是具体的配置内容:

#配置springboot对redis的依赖
spring.redis.host=192.168.124.34
spring.redis.port=6379
spring.redis.database=0
spring.redis.lettuce.pool.max-active=10000
spring.redis.jedis.pool.max-idle=50
spring.redis.jedis.pool.max-wait=-1ms
spring.redis.lettuce.pool.min-idle=0
spring.redis.password=

#设置jedis连接池
spring.redis.jedis.pool.max-active=50
spring.redis.jedis.pool.min-idle=20

这里我们依然使用了192.168.124.34这台服务器作为redis的服务器

简单回顾一下,这是之前的服务架构

SpringBoot项目之基于Cookie传输sessionId的分布式会话解决方案(三)

5)一切就绪,然后本地测试无误之后打包上传到服务器 

6)对应我们的服务器上需要安装好redis,具体的安装过程这里就不再多说。但是有几个地方需要注意:

我们安装好redis之后,【redis.conf】需要做一下简单修改:

a.将原来的   bind 127.0.0.1   修改为:    bind 192.168.124.34

b.将原来的   protected-mode yes  修改为  protected-mode no

不然可能不允许远程连接访问

然后后台启动我们的redis

src/redis-server ./redis.conf &

7)分别启动我们新上传的项目,然后访问我们的nginx服务器,具体看下面我们的演示:

首先访问我们的nginx服务器静态资源地址,我们进行登陆:

http://192.168.124.35/resources/login.html

SpringBoot项目之基于Cookie传输sessionId的分布式会话解决方案(三)

登陆成功可以看到我们的redis中已经存入了session

SpringBoot项目之基于Cookie传输sessionId的分布式会话解决方案(三)

再次下单提示成功,不再提示需要重新登陆

SpringBoot项目之基于Cookie传输sessionId的分布式会话解决方案(三)

至此我们基于Cookie传输sessionId的分布式会话解决方案成功