文章目录
- 1.Redis页面缓存
- 1.思路分析
- 2.首先记录一下目前访问商品列表页的QPS
- 1.线程组配置10000次请求
- 2.请求配置
- 3.开始压测
- 1.压测第一次 平均QPS为612
- 2.压测第二次 平均QPS为615
- 3.压测第三次 平均QPS为617
- 3.然后记录一下访问商品详情页的QPS
- 1.线程组配置10000次请求
- 2.请求配置
- 3.开始压测
- 1.压测第一次 平均QPS为633
- 2.压测第二次 平均QPS为642
- 3.压测第三次 平均QPS为641
- 4.商品列表页Redis缓存优化
- 1.GoodsController.java
- 2.启动报错 Error creating bean with name 'requestMappingHandlerMapping' defined in class path resource
- 3.发现是produces写错了,将冒号换成分号
- 4.重新启动测试
- 1.登录后访问 http://localhost:9092/seckill/goods/toList
- 2.此时如果在数据库中修改信息,在60s内是不会刷新的
- 5.商品详情页Redis缓存优化
- 1.GoodsController.java
- 2.测试
- 1.登录后访问详情页
- 2.在Redis中也有了缓存
- 6.压力测试
- 1.清空Redis
- 2.清空用户表
- 3.启动应用,重新生成2000个用户
- 4.对访问列表页进行压测
- 1.发现平均QPS只有80,比直接走数据库还慢
- 2.由于六台机器都开启了RDB和AOF的持久化策略,现在分别将其关闭,然后重启redis
- 3.再次压测,还是80
- 4.那么就可能是网络原因了,因为服务器都在北京,所以将服务部署到生产环境然后再进行压力测试
- 2.生产环境的压力测试
- 1.首先将GoodsController.java的从db查询商品列表打开
- 2.部署上线
- 1.激活环境为prod
- 2.maven打包
- 3.上传到服务器然后重新启动
- 3.UserUtil.java 获取用户信息
- 1.修改环境变量
- 2.获取cookie
- 4.准备压测
- 1.http请求默认值
- 2.http请求信息
- 3.修改cookie的域
- 4.开始压测5000次请求,QPS为55
- 5.使用redis缓存页面来优化并重新部署
- 6.再次压测,QPS为80,有所提升
- 7.关于Redis缓存页面与DB的数据同步问题
- 3.对象缓存问题解决
- 1.问题分析
- 2.具体实现
- 1.UserService.java
- 2.UserServiceImpl.java
- 3.UserController.java
- 3.测试
- 1.登录一下,得到票据
- 2.Redis中有该用户信息
- 3.更新密码 http://localhost:9092/seckill/user/updatePassword?userTicket=4dfaea799a9b438ea96ef61f7da435e3&password=666666
- 4.刷新Redis,用户信息被删除
1.Redis页面缓存
1.思路分析
2.首先记录一下目前访问商品列表页的QPS
1.线程组配置10000次请求
2.请求配置
3.开始压测
1.压测第一次 平均QPS为612
2.压测第二次 平均QPS为615
3.压测第三次 平均QPS为617
3.然后记录一下访问商品详情页的QPS
1.线程组配置10000次请求
2.请求配置
3.开始压测
1.压测第一次 平均QPS为633
2.压测第二次 平均QPS为642
3.压测第三次 平均QPS为641
4.商品列表页Redis缓存优化
1.GoodsController.java
@Resource
GoodsService goodsService;
@Resource
private RedisTemplate redisTemplate;
@Resource
private ThymeleafViewResolver thymeleafViewResolver;
// 进入到商品首页-使用redis优化
@RequestMapping(value = "/toList", produces = "text/html;charset=UTF-8")
@ResponseBody
public String toList(Model model, User user, HttpServletRequest request, HttpServletResponse response) {
// 判断是否有用户信息
if (null == user) {
return "login";
}
// 先从redis中获取页面,如果有则直接返回
String html = (String) redisTemplate.opsForValue().get("goodsList");
if (StringUtils.hasText(html)) {
return html;
}
// 如果没有就从数据库中查询,然后存入redis中
// ------------------------------db查询商品列表------------------------------
// 查询商品列表
model.addAttribute("goodsList", goodsService.findGoodsVo());
// 将用户信息存入model中
model.addAttribute("user", user);
// ------------------------------db查询商品列表------------------------------
// 渲染页面
// 1.首先构建一个webContext对象,用来存放model
WebContext context = new WebContext(request, response, request.getServletContext(), request.getLocale(), model.asMap());
// 2.渲染页面
html = thymeleafViewResolver.getTemplateEngine().process("goodsList", context);
// 3.判断html是否为空,不为空则存入redis中,设置过期时间为60s
if (StringUtils.hasText(html)) {
redisTemplate.opsForValue().set("goodsList", html, 180, TimeUnit.SECONDS);
}
return html;
}
2.启动报错 Error creating bean with name ‘requestMappingHandlerMapping’ defined in class path resource
3.发现是produces写错了,将冒号换成分号
4.重新启动测试
1.登录后访问 http://localhost:9092/seckill/goods/toList
2.此时如果在数据库中修改信息,在60s内是不会刷新的
5.商品详情页Redis缓存优化
1.GoodsController.java
// 进入到商品详情页
@RequestMapping(value = "/toDetail/{goodsId}", produces = "text/html;charset=UTF-8")
@ResponseBody
public String toDetail(Model model, User user, @PathVariable Long goodsId, HttpServletRequest request, HttpServletResponse response) {
// 判断是否有用户信息
if (null == user) {
return "login";
}
// 先从redis中获取页面,如果有则直接返回
String html = (String) redisTemplate.opsForValue().get("goodsDetail:" + goodsId);
// 如果有则直接返回
if (StringUtils.hasText(html)) {
return html;
}
// 如果没有就从数据库中查询,然后存入redis中
// ------------------------------db查询商品详情------------------------------
// 查询商品详情
GoodsVo goodsVoByGoodsId = goodsService.findGoodsVoByGoodsId(goodsId);
model.addAttribute("goods", goodsVoByGoodsId);
// secKillStatus:秒杀状态 0:未开始 1:进行中 2:已结束
// remainSeconds:秒杀剩余时间 >0:未开始 0:进行中 -1:已结束
// 获取该商品的秒杀开始时间和结束时间
long startAt = goodsVoByGoodsId.getStartDate().getTime();
long endAt = goodsVoByGoodsId.getEndDate().getTime();
long now = System.currentTimeMillis();
// 根据当前时间与秒杀开始时间和结束时间的比较,判断秒杀状态
int secKillStatus = 0;
int remainSeconds = 0;
if (now < startAt) {
// 秒杀未开始
secKillStatus = 0;
remainSeconds = (int) ((startAt - now) / 1000);
} else if (now > endAt) {
// 秒杀已结束
secKillStatus = 2;
remainSeconds = -1;
} else {
// 秒杀进行中
secKillStatus = 1;
remainSeconds = 0;
}
// 将秒杀状态和剩余时间存入model中,返回到前端
model.addAttribute("secKillStatus", secKillStatus);
model.addAttribute("remainSeconds", remainSeconds);
// 将用户信息存入model中,返回到前端
model.addAttribute("user", user);
// ------------------------------db查询商品详情------------------------------
// 渲染页面
// 1.首先构建一个webContext对象,用来存放model
WebContext context = new WebContext(request, response, request.getServletContext(), request.getLocale(), model.asMap());
// 2.渲染页面
html = thymeleafViewResolver.getTemplateEngine().process("goodsDetail", context);
// 3.判断html是否为空,不为空则存入redis中,设置过期时间为60s
if (StringUtils.hasText(html)) {
redisTemplate.opsForValue().set("goodsDetail:" + goodsId, html, 180, TimeUnit.SECONDS);
}
return html;
}
2.测试
1.登录后访问详情页
2.在Redis中也有了缓存
6.压力测试
1.清空Redis
2.清空用户表
3.启动应用,重新生成2000个用户
4.对访问列表页进行压测
1.发现平均QPS只有80,比直接走数据库还慢
2.由于六台机器都开启了RDB和AOF的持久化策略,现在分别将其关闭,然后重启redis
3.再次压测,还是80
4.那么就可能是网络原因了,因为服务器都在北京,所以将服务部署到生产环境然后再进行压力测试
2.生产环境的压力测试
1.首先将GoodsController.java的从db查询商品列表打开
2.部署上线
1.激活环境为prod
2.maven打包
3.上传到服务器然后重新启动
3.UserUtil.java 获取用户信息
1.修改环境变量
2.获取cookie
4.准备压测
1.http请求默认值
2.http请求信息
3.修改cookie的域
4.开始压测5000次请求,QPS为55
5.使用redis缓存页面来优化并重新部署
6.再次压测,QPS为80,有所提升
7.关于Redis缓存页面与DB的数据同步问题
3.对象缓存问题解决
1.问题分析
在校验用户是否登录时,会根据cookie在Redis中查询用户信息,但是如果在DB中的用户信息更改了,那么就会发生数据不一致的问题
2.具体实现
1.UserService.java
/**
* 更新密码
* @param userTicket
* @param password
* @param request
* @param response
* @return
*/
public RespBean updatePassword(String userTicket, String password, HttpServletRequest request, HttpServletResponse response);
2.UserServiceImpl.java
@Override
public RespBean updatePassword(String userTicket, String password, HttpServletRequest request, HttpServletResponse response) {
// 根据ticket获取用户
User user = getUserByCookie(userTicket, request, response);
if (null == user) {
throw new GlobalException(RespBeanEnum.MOBILE_NOT_EXIST);
}
// 更新密码
user.setPassword(MD5Util.inputPassToDBPass(password, user.getSlat()));
int result = userMapper.updateById(user);
if (1 == result) {
// 删除redis中的用户信息
redisTemplate.delete("user:" + userTicket);
return RespBean.success();
}
return RespBean.error(RespBeanEnum.PASSWORD_UPDATE_FAIL);
}
3.UserController.java
// 更新密码
@RequestMapping("/updatePassword")
@ResponseBody
public RespBean updatePassword(String userTicket, String password, HttpServletRequest request, HttpServletResponse response) {
return userService.updatePassword(userTicket, password, request, response);
}