1.页面缓存
想象一下秒杀的场景,僧多粥少,在秒杀将要开始的半分钟内,用户可能会不断刷新秒杀页面,此时对于页面访问的流量将达到顶峰。
解决方案
- 服务端缓存页面:
在服务端手动渲染商品详情页面,直接返回html给前端
将页面加入redis缓存,设置合适的,较短的有效期,比如60s
每次请求都先从redis中取,取不到再渲染并返回 - 客户端缓存页面:
同时可以设置浏览器缓存该页面60s,这样在此期间浏览器、服务器就没有数据交互了
@GetMapping(value="/goods2",produces="text/html")
@ResponseBody
public String findAllGoods2(Model model,HttpServletRequest request, HttpServletResponse response){
//TODO 此处想设置浏览器缓存该页面60s,但是好像不管用啊
response.setHeader("Cache-Control", "max-age:60");
//先从缓存中取,没有再渲染
String html = redisTemplate.opsForValue().get("goods");
if(!StringUtils.isEmpty(html)) {
return html;
}
List<Goods> goods = goodsService.findAllGoods();
model.addAttribute("goods",goods);
//设置秒杀开始时间 为了使用缓存,当前时间使用客户端自己的时间
model.addAttribute("targetTime",Constant.BARGAIN_DASH_START_TIME);
//手动渲染
WebContext ctx = new WebContext(request,response,
request.getServletContext(),request.getLocale(), model.asMap());
html = thymeleafViewResolver.getTemplateEngine().process("goods", ctx);
//将结果加入redis,设置有效期60s
if(!StringUtils.isEmpty(html)) {
redisTemplate.opsForValue().set("goods", html, 60, TimeUnit.SECONDS);
}
return html;
}
由于商品页面比较固定,客户端对于页面实时更新要求不会很高,所以可以加入redis缓存起来。
由于redis缓存了页面,所以页面的倒计时效果只能取客户端的时间作为当前时间了。在真正秒杀的时候,服务端会对秒杀是否开始了做一次判断。其实,即使采用服务端时间做当前时间,倒计时也不可能完全准确,最好还是后端再做判断,所以这里直接采用客户端的时间做当前时间,也是合适的。
我想在服务端指定客户端缓存该页面60s,但是实际测试好像没效果,仍然会每次都发get请求,可能是哪里没有设置对吧。如果可以在浏览器缓存,将会更进一步提高性能。
这里前端页面倒计时效果,使用的是tictac,是从github上搜索来的,这种小组件,直接上github上找,比百度要更快更好。
压力测试
可以看到,该优化的效果还是极为明显的。
2.其他优化点
目前代码主要是从后端进行了优化,如果有专业前端支持,前后端共同协作,效果会更好。
- 这里thymeleaf的渲染毕竟还是在服务端完成的,如果有前端人员支持的话,可以考虑下最近流行的angular、veu,实现彻底的前后端分离。前端发起请求,后端将商品信息缓存到redis,前端渲染页面,缓存页面。将工作量分担给无数个客户端,减轻服务端压力。
- 静态资源压缩,js css html等进行压缩,降低流量消耗
- nginx静态资源缓存
附:所有代码在github上
网友评论