当前位置:网站首页>秒杀系统2-Redis解决分布式Session问题
秒杀系统2-Redis解决分布式Session问题
2022-07-03 15:18:00 【ジ你是我永远のbugグ】
前言
如果操作在一台Tomcat上,是没有问题的,但是当我们部署多台系统,配合Nginx的时候会出现用户登录的问题。
原因
由于Nginx 使用负载均衡策略,将请求分发到后端,
也就是说,在Tomcat 1 登录后,用户信息存在Tomcat1的Session里,下次请求又到了Tomcat2上,这时Tomcat2上Session里还没有用户信息,就要去登录。

解决
redis 安装教程自学
Redis实现分布式Session
方法1、使用SpringSession实现 (这个方法跳转不了页面)
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>
<!-- spring-session 依赖 -->
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
2、配置 Redis
spring:
# thymeleaf关闭缓存
thymeleaf:
cache: false
# MySql数据源配置
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/secKill?useUnicode=true&characterEncoding=utf-8&useSSL=false
username: root
password: 123456
# 最快的连接池
hikari:
# 连接池的名称
pool-name: DateHikariCP
# 最小空闲连接数
minimum-idle: 5
# 空闲连接存活最大时间 默认600000(10分钟)
idle-timeout: 1800000
# 最大连接数 默认10
maximum-pool-size: 10
# 从连接池返回的连接自动提交
auto-commit: true
# 连接最大存活时间 0代表永久存活,默认 1800000(分钟)
max-lifetime: 1800000
# 连接超时时间 默认30000(30秒)
connection-timeout: 30000
# 测试连接是否可以的查询语句
connection-test-query: SELECT 1
# Redis配置
redis:
host: 127.0.0.1
port: 6379
# 使用的数据库 0
database: 0
# 连接超时时间
timeout: 10000ms
lettuce:
# 连接池的配置
pool:
# 最大连接数 8
max-active: 8
# 最大连接的阻塞时间
max-wait: 10000ms
# 最大空闲连接
max-idle: 200
# 最小空闲连接
min-idle: 5
# mybatis-plus 配置
mybatis-plus:
# 配置 Mapper.xml的映射文件路径
mapper-locations: classpath*:/mapper/*.xml # mybatis SQL 打印(接口方法在的包,不是 mapper.xml所在的包) logging: level: com.example.seckill.mapper: DEBUG 这个时候 就OK 了,登录后,Session就会存进 Redis - 0(库) -spring Session 中
此时 注释掉我们代码中的
// /**
// * 校验用户成功后生成 cookie,将cookie 与用户存进 session中
// */
// // 使用UUID生成 cookie
// String cookie = UUIDUtil.uuid();
// // 将 cookie 和用户存进 session 中
// request.getSession().setAttribute(cookie,user);
// // 设置 cookie
// CookieUtil.setCookie(request,response,"userCookie",cookie);
再次登录 发现也是会存进Redis中。
方法2、将用户信息存进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、redis 的配置 和方法1一样
3、Redsi配置类
package com.example.seckill.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
/** * 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类型value序列化
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
//注入连接工厂
redisTemplate.setConnectionFactory(redisConnectionFactory);
return redisTemplate;
}
}
4、json工具类
这个很重要,如果没有的话 从redis中获取的user信息匹配不对,跳转不了 商品页面
package com.example.seckill.util;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
import java.util.List;
/** * Json工具类 * * @author zhoubin * @since 1.0.0 */
public class JsonUtil {
private static ObjectMapper objectMapper = new ObjectMapper();
/** * 将对象转换成json字符串 * * @param obj * @return */
public static String object2JsonStr(Object obj) {
try {
return objectMapper.writeValueAsString(obj);
} catch (JsonProcessingException e) {
//打印异常信息
e.printStackTrace();
}
return null;
}
/** * 将字符串转换为对象 * * @param <T> 泛型 */
public static <T> T jsonStr2Object(String jsonStr, Class<T> clazz) {
try {
return objectMapper.readValue(jsonStr.getBytes("UTF-8"), clazz);
} catch (JsonParseException e) {
e.printStackTrace();
} catch (JsonMappingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
/** * 将json数据转换成pojo对象list * <p>Title: jsonToList</p> * <p>Description: </p> * * @param jsonStr * @param beanType * @return */
public static <T> List<T> jsonToList(String jsonStr, Class<T> beanType) {
JavaType javaType = objectMapper.getTypeFactory().constructParametricType(List.class, beanType);
try {
List<T> list = objectMapper.readValue(jsonStr, javaType);
return list;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
5、修改 UserServiceImpl代码
在验证用户后,将user 存进redis
并添加通过cookie 获取user的方法
package com.example.seckill.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.seckill.common.RespBean;
import com.example.seckill.common.RespBeanEnum;
import com.example.seckill.controller.parm.LoginRequestParam;
import com.example.seckill.exception.GlobalException;
import com.example.seckill.mapper.UserMapper;
import com.example.seckill.pojo.User;
import com.example.seckill.service.IUserService;
import com.example.seckill.util.CookieUtil;
import com.example.seckill.util.JsonUtil;
import com.example.seckill.util.MD5Util;
import com.example.seckill.util.UUIDUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/** * <p> * 用户表 服务实现类 * </p> * * @author jobob * @since 2022-06-13 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
@Autowired(required = false)
UserMapper userMapper;
@Autowired
private RedisTemplate redisTemplate;
@Override
public RespBean doLogin(LoginRequestParam param, HttpServletRequest request, HttpServletResponse response) {
String password = param.getPassword();
String mobile = param.getMobile();
User user = userMapper.selectById(mobile);
if (null == user){
throw new GlobalException(RespBeanEnum.LOGIN_ERROR);
}
if (!MD5Util.formPassToDBPass(password,user.getSalt()).equals(user.getPassword())){
throw new GlobalException(RespBeanEnum.LOGIN_ERROR);
}
/** * 校验用户成功后生成 cookie,将cookie 与用户存进 session中 */
// 使用UUID生成 cookie
String cookie = UUIDUtil.uuid();
// 将 cookie 和用户存进 session 中
// request.getSession().setAttribute(cookie,user);
redisTemplate.opsForValue().set("user:" + cookie, JsonUtil.object2JsonStr(user));
// 设置 cookie
CookieUtil.setCookie(request,response,"userCookie",cookie);
return RespBean.success(cookie);
}
@Override
public User getUserByCookie(String cookie, HttpServletRequest request, HttpServletResponse response) {
if (StringUtils.isEmpty(cookie)){
return null;
}
String userJson = (String) redisTemplate.opsForValue().get("user:" + cookie);
User user = JsonUtil.jsonStr2Object(userJson, User.class);
if (null != user){
// 设置 cookie
CookieUtil.setCookie(request,response,"userCookie",cookie);
}
return user;
}
}
6、商品Controller
package com.example.seckill.controller;
import com.example.seckill.common.RespBean;
import com.example.seckill.controller.parm.LoginRequestParam;
import com.example.seckill.pojo.User;
import com.example.seckill.service.IGoodsService;
import com.example.seckill.service.IUserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.CookieValue;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
@RequestMapping("/goods")
@Controller
@Slf4j
public class GoodsController {
@Autowired
IGoodsService goodsService;
@Autowired
IUserService userService;
/** * 跳转商品页 * @param * @param model * @param cookie * @return */
@RequestMapping("/toList")
public String toList(HttpServletRequest request, HttpServletResponse response, Model model, @CookieValue("userCookie") String cookie){
if (StringUtils.isEmpty(cookie)){
//如果 cookie为 空 跳转到 登录页面
return "login";
}
// 从session 中获取用户
// User user = (User) session.getAttribute(cookie);
User user = userService.getUserByCookie(cookie, request, response);
if (null == user){
// 如果用户信息为空 跳转登录
return "login";
}
// 将用户信息 传到前端页面
model.addAttribute("user",user);
return "goodsList";
}
}
以上避免每个接口都要去完成 根据Cookie获取User信息,避免代码冗余,特做优化:
1、UserArgumentResolvers类
package com.example.seckill.config;
import com.example.seckill.pojo.User;
import com.example.seckill.service.IUserService;
import com.example.seckill.util.CookieUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.MethodParameter;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/** * 自定义用户参数 */
@Component
public class UserArgumentResolvers implements HandlerMethodArgumentResolver {
@Autowired
IUserService userService;
@Override
public boolean supportsParameter(MethodParameter parameter) {
Class<?> parameterType = parameter.getParameterType();
return parameterType == User.class;
}
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class);
String cookie = "486ceb27176a485f85e3c3d1f4f35e77";
// String cookie = CookieUtil.getCookieValue(request, "userCookie");
if (StringUtils.isEmpty(cookie)){
//如果 cookie为 空 跳转到 登录页面
return null;
}
return userService.getUserByCookie(cookie, request, response);
}
}
2、
package com.example.seckill.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.List;
/** * MVC的配置类 * */
@Configuration
//@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
@Autowired
UserArgumentResolvers userArgumentResolvers;
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers){
resolvers.add(userArgumentResolvers);
}
}
3、goodController
package com.example.seckill.controller;
import com.example.seckill.common.RespBean;
import com.example.seckill.controller.parm.LoginRequestParam;
import com.example.seckill.pojo.User;
import com.example.seckill.service.IGoodsService;
import com.example.seckill.service.IUserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.CookieValue;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
@RequestMapping("/goods")
@Controller
@Slf4j
public class GoodsController {
@Autowired
IGoodsService goodsService;
@Autowired
IUserService userService;
/** * 跳转商品页 * @param * @param model * @param * @return */
@RequestMapping("/toList")
public String toList(Model model,User user){
// 将用户信息 传到前端页面
model.addAttribute("user",user);
return "goodsList";
}
}
问题:这里我遇到一个问题,每次生成Cookie存进Redis是正常的,但是登录成功后调用其他接口 通过CookieUtil 获取的Cookie并不是本次登录生成的Cookie 就很烦~ 我对这块不太了解 所以在保证redis中存在这个cookie ,将cookie写死了
边栏推荐
- 什么是one-hot encoding?Pytorch中,将label变成one hot编码的两种方式
- [wechat applet] wxss template style
- 视觉上位系统设计开发(halcon-winform)
- 开启 Chrome 和 Edge 浏览器多线程下载
- Incluxdb2 buckets create database
- [pytorch learning notes] datasets and dataloaders
- Analysis of development mode process based on SVN branch
- 什么是embedding(把物体编码为一个低维稠密向量),pytorch中nn.Embedding原理及使用
- "Seven weapons" in the "treasure chest" of machine learning: Zhou Zhihua leads the publication of the new book "machine learning theory guide"
- Jvm-09 byte code introduction
猜你喜欢
![[pytorch learning notes] datasets and dataloaders](/img/c0/9cd539caff34db3cccc44505bbe3c5.png)
[pytorch learning notes] datasets and dataloaders

视觉上位系统设计开发(halcon-winform)-3.图像控件

Composite type (custom type)

Redis cache penetration, cache breakdown, cache avalanche solution

Solve the problem that pushgateway data will be overwritten by multiple push
![[Yu Yue education] scientific computing and MATLAB language reference materials of Central South University](/img/83/922efb4f88843f1b7feaccf2b515b9.jpg)
[Yu Yue education] scientific computing and MATLAB language reference materials of Central South University

Incluxdb2 buckets create database
![MySQL reports an error: [error] mysqld: file '/ mysql-bin. 010228‘ not found (Errcode: 2 “No such file or directory“)](/img/cd/2e4f5884d034ff704809f476bda288.png)
MySQL reports an error: [error] mysqld: file '/ mysql-bin. 010228‘ not found (Errcode: 2 “No such file or directory“)

Jvm-06-execution engine

Kubernetes will show you from beginning to end
随机推荐
Composite type (custom type)
【Transform】【NLP】首次提出Transformer,Google Brain团队2017年论文《Attention is all you need》
Yolov5 series (I) -- network visualization tool netron
使用Tengine解决负载均衡的Session问题
Summary of JVM knowledge points
406. Reconstruct the queue according to height
Finally, someone explained the financial risk management clearly
[wechat applet] wxss template style
Matplotlib drawing label cannot display Chinese problems
Concurrency-02-visibility, atomicity, orderliness, volatile, CAS, atomic class, unsafe
Characteristics of MySQL InnoDB storage engine -- Analysis of row lock
Kubernetes - yaml file interpretation
Global and Chinese market of trimethylamine 2022-2028: Research Report on technology, participants, trends, market size and share
详解指针进阶1
Concurrency-01-create thread, sleep, yield, wait, join, interrupt, thread state, synchronized, park, reentrantlock
Centos7 deployment sentry redis (with architecture diagram, clear and easy to understand)
高并发下之redis锁优化实战
[transformer] Introduction - the original author of Harvard NLP presented the annotated transformer in the form of line by line implementation in early 2018
Redis lock Optimization Practice issued by gaobingfa
视觉上位系统设计开发(halcon-winform)-5.相机