SpringBoot项目之基于Cookie传输sessionId的分布式会话解决方案(三)
上一章节讲到【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的服务器
简单回顾一下,这是之前的服务架构
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
登陆成功可以看到我们的redis中已经存入了session
再次下单提示成功,不再提示需要重新登陆
至此我们基于Cookie传输sessionId的分布式会话解决方案成功