页面缓存的原理:
普通要想生成一个页面,需要该页面在springboot的templates目录下,然后通过springboot的内部渲染,视图解析器将页面渲染回客户端,这中间会花费很长的时间。
但如果将整个页面的代码转化为字符串,存到redis中,当请求一个页面时,通过ResponseBody注解,将该字符串直接返回,由客户端自己渲染成页面,那么服务器的压力就会小很多,有效的解决并发。
存在的问题:
需要合理设置redis缓存的时间,如果设置时间太长,会导致当页面数据本该变化的时候却没有改变,因为缓存的一定是一个静态页面。
实现代码:
@Controller @RequestMapping("/goods") public class GoodsController { @Autowired private MiaoshaUserService miaoshaUserService; @Autowired private GoodsService goodsService; @Autowired private RedisService redisService; @Autowired private ThymeleafViewResolver thymeleafViewResolver; @Autowired ApplicationContext applicationContext; @RequestMapping(value = "/to_list",produces = "text/html") @ResponseBody public String list(Model model, MiaoshaUser user, HttpServletRequest request,HttpServletResponse response){ model.addAttribute("user",user); String html = redisService.get(GoodsKey.getGoodsList, "", String.class); if(!StringUtils.isEmpty(html)){ System.out.println("从缓存中取"); return html; } List<GoodsVo> goodsList = goodsService.listGoodsVo(); model.addAttribute("goodsList",goodsList); SpringWebContext ctx = new SpringWebContext(request,response,request.getServletContext(),request.getLocale(),model.asMap(),applicationContext); //手动渲染 html = thymeleafViewResolver.getTemplateEngine().process("goods_list", ctx); if(!StringUtils.isEmpty(html)){ redisService.set(GoodsKey.getGoodsList,"",html); } System.out.println("从数据库中取"); return html; } @RequestMapping(value = "/to_detail/{goodsId}", produces = "text/html") @ResponseBody public String detail(HttpServletRequest request,HttpServletResponse response,Model model, MiaoshaUser user, @PathVariable("goodsId") long goodsId){ model.addAttribute("user",user); String html=redisService.get(GoodsKey.getGoodsDetail,""+goodsId,String.class); if(!StringUtils.isEmpty(html)){ System.out.println("从缓存中取"); return html; } GoodsVo goods = goodsService.getGoodsVoByGoodsId(goodsId); model.addAttribute("goods", goods); //得到秒杀的开始时间、结束时间、以及当前时间 long startAt = goods.getStartDate().getTime(); long endAt = goods.getEndDate().getTime(); long now = System.currentTimeMillis(); //设置剩余时间 int remainSeconds=0; //设置秒杀状态 int miaoshaStatus=0; //判断 if(now<startAt){ //秒杀还没开始 miaoshaStatus=0; remainSeconds= (int) ((startAt-now)/1000); }else if(now>endAt){ //秒杀已经结束 miaoshaStatus=2; remainSeconds=-1; }else { //秒杀正在进行 miaoshaStatus=1; remainSeconds=0; } model.addAttribute("miaoshaStatus",miaoshaStatus); model.addAttribute("remainSeconds",remainSeconds); SpringWebContext ctx = new SpringWebContext(request,response,request.getServletContext(),request.getLocale(),model.asMap(),applicationContext); //手动渲染 html = thymeleafViewResolver.getTemplateEngine().process("goods_detail", ctx); if(!StringUtils.isEmpty(html)){ redisService.set(GoodsKey.getGoodsDetail,""+goodsId,html); } System.out.println("从数据库中取"); return html; } }
对象缓存:
对象缓存,如果只是查询某个对象,则比较简单,判断缓存中有没有对象,如果有就返回,如果没有就从数据库中取到,然后放入缓存中,然后返回。
public MiaoshaUser getById(long id){ MiaoshaUser user = redisService.get(MiaoshaUserKey.getById, ""+id, MiaoshaUser.class); if(user!=null){ System.out.println("从缓存取"); return user; } user = miaoshaUserDao.getById(id); if(user!=null){ redisService.set(MiaoshaUserKey.getById,""+id,user); } System.out.println("从数据库取"); return user; }
如果涉及到修改对象中的某个值,则需要做的事情比较多,考虑到有并发的存在,因此,我们需要先修改数据库的内容,然后修改缓存的内容。
比如一个修改密码的方法
public boolean updatePassword(String token, long id, String formPass){ //取出user MiaoshaUser user = getById(id); if(user==null){ throw new GlobalException(CodeMsg.MOBILE_NOT_EXIST); } user.setPassword(MD5Util.formPassToDBPass(formPass,user.getSalt())); //更新数据库 miaoshaUserDao.update(user); //处理缓存, redisService.delete(MiaoshaUserKey.getById,""+id); redisService.set(MiaoshaUserKey.token,token,user); return true; }