分布式Session问题
由于Nginx使用默认负载均衡策略(轮询),请求会按照时间顺序逐一发到后端应用。在Tomcat1登录后,用户信息存放在Tomcat1的Session里,后来被分到了Tomcat2上,这时候Tomcat2的Session没有用户信息,所以需要重新登录。
解决方案有:Session复制、前端存储、Session粘滞、后端集中存储这些方法。几种方法各有优缺点,需要我们根据实际情况选择。
一、使用SprignSession实现
1. 添加依赖
spring data redis、对象池commons-pool2、spring-session
<!-- spring data redis 依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- commons-pool2 对象池依赖 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<!-- spring-session 依赖 -->
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
2. 添加配置
redis:
# 超时时间
timeout: 10000ms
# 服务器地址
host: 192.168.222.129
# 服务器端口,默认6379
port: 6379
# 数据库,默认0
database: 0
# 密码
password: root
lettuce:
pool:
#最大连接数,默认8
max-active: 1024
#最大连接阻塞等待时间,默认-1
max-wait: 10000ms
#最大空闲连接,默认8
max-idle: 200
#最小空闲连接,默认0
min-idle: 5
二、将用户信息存入Redis
1. 添加依赖
<!-- spring data redis 依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- commons-pool2 对象池依赖 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
2. 添加配置
这部分和方法一一样。
3. redis配置类
主要完成对象的序列化
/**
* @author 47roro
* @create 2022/4/5
* @description: Redis配置类
*/
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory){
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
//Key序列化
redisTemplate.setKeySerializer(new StringRedisSerializer());
//Value序列化
redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
//Hash序列化
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
//注入连接工厂
redisTemplate.setConnectionFactory(redisConnectionFactory);
return redisTemplate;
}
}
4. 修改代码
之前是将cookie值存在了session中,现在把cookie值存入redis
/**
* 服务实现类
* @author 47roro
* @since 2022-04-03
*/
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
@Autowired
private UserMapper userMapper;
@Autowired
private RedisTemplate redisTemplate;
/**
* 登录
* @author 47roro
* @date 2022/4/3
* @param loginVo
* @return com.example.seckill.vo.RespBean
**/
@Override
public RespBean doLogin(LoginVo loginVo, HttpServletRequest request, HttpServletResponse response) {
String mobile = loginVo.getMobile();
String password = loginVo.getPassword();
User user = userMapper.selectById(mobile);
if(null == user){
throw new GlobalException(RespBeanEnum.LOGIN_ERROR);
}
if(!MD5Util.frompassToDBPass(password, user.getSalt()).equals(user.getPassword())){
throw new GlobalException(RespBeanEnum.LOGIN_ERROR);
}
// 生成cookie
String cookie = UUIDUtil.uuid();
// 将用户信息存入redis
redisTemplate.opsForValue().set("user:" + cookie, user);
// 使用session
//request.getSession().setAttribute(cookie, user);
CookieUtil.setCookie(request, response, "userCookie", cookie);
return RespBean.success();
}
/**
* 根据Cookie获取用户
* @author 47roro
* @date 2022/4/5
* @param userCookie
* @param request
* @param response
* @return com.example.seckill.pojo.User
**/
@Override
public User getUserByCookie(String userCookie, HttpServletRequest request, HttpServletResponse response) {
if(!StringUtils.hasLength(userCookie)){
return null;
}
User user = (User)redisTemplate.opsForValue().get("user:" + userCookie);
if(user != null){
CookieUtil.setCookie(request, response, "userCookie", userCookie);
}
return user;
}
}
商品页面跳转获取用户方式修改
/**
* @author 47roro
* @create 2022/4/3
* @description: 商品页面跳转
*/
@Controller
@RequestMapping("/goods")
public class GoodsController {
@Autowired
private UserServiceImpl userService;
/**
* 跳转到商品列表页面
* @author 47roro
* @date 2022/4/3
* @param request,response
* @param model
* @param cookie
* @return java.lang.String
**/
@RequestMapping("/toList")
public String toList(HttpServletRequest request, HttpServletResponse response, Model model, @CookieValue("userCookie") String cookie){
if(StringUtils.isEmpty(cookie)){
return "login";
}
//通过session获取cookie
//User user = (User) session.getAttribute(cookie);
//通过cookie获取用户
User user = userService.getUserByCookie(cookie, request, response);
if(null == user){
return "login";
}
model.addAttribute("user", user);
return "goodsList";
}
}