java spike Series (2) - static pages Technology


Foreword

Code fragments are introduced by rendering the server, a client rendering, the object cache written in three ways.
Fragment for reference, the need to achieve self-adaptation according to the service scenario, but the idea is the same.




First, the server-side rendering


1, the interface returns to set html page

@Autowired
ThymeleafViewResolver thymeleafViewResolver;
@Autowired
ApplicationContext applicationContext;

@RequestMapping(value="/to_list", produces="text/html")
@ResponseBody
public String goodsList() {
    // 业务逻辑
}


2, the cache fetch start, there returns.

//取缓存
String html = redisService.get(GoodsKey.getGoodsList, "", String.class);
if(!StringUtils.isEmpty(html)) {
    return html;
}


3, no cache, rendering it manually.

springboot1.5.x wording:

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);

springboot2.x wording:

WebContext ctx = new WebContext(request, response, request.getServletContext(),  request.getLocale(), model.asMap());
//手动渲染
html = thymeleafViewResolver.getTemplateEngine().process("goods_list", ctx);


4, then the final rendering content added to redis

if(!StringUtils.isEmpty(html)) {
    redisService.set(GoodsKey.getGoodsList, "", html);
}




Second, client rendering (Product Details page)


1, jump to the product details page to find the path to modify a static html path.

In the product list page, locate the jump to the dynamic path details page directly modify a static path, suffix htm or shtml, html is not a word you can, because application.properties will generally configured as a suffix html templates are accessible folder.
Note that the details of the code links to a static page goods_detail.htm:

<body>

<div class="panel panel-default" style="height:100%;background-color:rgba(222,222,222,0.8)">
  <div class="panel-heading">秒杀商品列表</div>
  <table class="table" id="goodslist">
    <tr><td>商品名称</td><td>商品图片</td><td>商品原价</td><td>秒杀价</td><td>库存数量</td><td>详情</td></tr>
    <tr  th:each="goods,goodsStat : ${goodsList}">  
                <td th:text="${goods.goodsName}"></td>  
                <td ><img th:src="@{${goods.goodsImg}}" width="100" height="100" /></td>  
                <td th:text="${goods.goodsPrice}"></td>  
                <td th:text="${goods.miaoshaPrice}"></td>  
                <td th:text="${goods.stockCount}"></td>
                <td><a th:href="'/goods_detail.htm?goodsId='+${goods.id}">详情</a></td>  
     </tr>  
  </table>
</div>
</body>


2, according to the path 1, the new static directory under a goods_detail.htm file, template language on the original dynamic pages are removed and replaced with id = "xx", then use ajax to render data to a static file.

Original dynamic page:

<div class="panel panel-default">
  <div class="panel-heading">秒杀商品详情</div>
  <div class="panel-body">
    <span th:if="${user eq null}"> 您还没有登录,请登陆后再操作<br/></span>
    <span>没有收货地址的提示。。。</span>
  </div>
  <table class="table" id="goodslist">
    <tr>  
        <td>商品名称</td>  
        <td colspan="3" th:text="${goods.goodsName}"></td> 
     </tr>  
     <tr>  
        <td>商品图片</td>  
        <td colspan="3"><img th:src="@{${goods.goodsImg}}" width="200" height="200" /></td>  
     </tr>
     <tr>  
        <td>秒杀开始时间</td>  
        <td th:text="${#dates.format(goods.startDate, 'yyyy-MM-dd HH:mm:ss')}"></td>
        <td id="miaoshaTip">    
            <input type="hidden" id="remainSeconds" th:value="${remainSeconds}" />
            <span th:if="${miaoshaStatus eq 0}">秒杀倒计时:<span id="countDown" th:text="${remainSeconds}"></span>秒</span>
            <span th:if="${miaoshaStatus eq 1}">秒杀进行中</span>
            <span th:if="${miaoshaStatus eq 2}">秒杀已结束</span>
        </td>
        <td>
            <form id="miaoshaForm" method="post" action="/miaosha/do_miaosha">
                <button class="btn btn-primary btn-block" type="submit" id="buyButton">立即秒杀</button>
                <input type="hidden" name="goodsId" th:value="${goods.id}" />
            </form>
        </td>
     </tr>
     <tr>  
        <td>商品原价</td>  
        <td colspan="3" th:text="${goods.goodsPrice}"></td>  
     </tr>
      <tr>  
        <td>秒杀价</td>  
        <td colspan="3" th:text="${goods.miaoshaPrice}"></td>  
     </tr>
     <tr>  
        <td>库存数量</td>  
        <td colspan="3" th:text="${goods.stockCount}"></td>  
     </tr>
  </table>
</div>

After the static page: you can see, the dynamic template language are removed, directly assigned by JS.

<div class="panel panel-default" style="height:100%;background-color:rgba(222,222,222,0.8)" >
  <div class="panel-heading">秒杀商品详情</div>
  <div class="panel-body">
    <span id="userTip"> 您还没有登录,请登陆后再操作<br/></span>
    <span>没有收货地址的提示。。。</span>
  </div>
  <table class="table" id="goodslist">
    <tr>  
        <td>商品名称</td>  
        <td colspan="3" id="goodsName"></td> 
     </tr>  
     <tr>  
        <td>商品图片</td>  
        <td colspan="3"><img  id="goodsImg" width="200" height="200" /></td>  
     </tr>
     <tr>  
        <td>秒杀开始时间</td>  
        <td id="startTime"></td>
        <td >   
            <input type="hidden" id="remainSeconds" />
            <span id="miaoshaTip"></span>
        </td>
        <td>
        <!--  
            <form id="miaoshaForm" method="post" action="/miaosha/do_miaosha">
                <button class="btn btn-primary btn-block" type="submit" id="buyButton">立即秒杀</button>
                <input type="hidden" name="goodsId"  id="goodsId" />
            </form>-->
            <div class="row">
                <div class="form-inline">
                    <img id="verifyCodeImg" width="80" height="32"  style="display:none" onclick="refreshVerifyCode()"/>
                    <input id="verifyCode"  class="form-control" style="display:none"/>
                    <button class="btn btn-primary" type="button" id="buyButton"onclick="getMiaoshaPath()">立即秒杀</button>
                </div>
            </div>
            <input type="hidden" name="goodsId"  id="goodsId" />
        </td>
     </tr>
     <tr>  
        <td>商品原价</td>  
        <td colspan="3" id="goodsPrice"></td>  
     </tr>
      <tr>  
        <td>秒杀价</td>  
        <td colspan="3"  id="miaoshaPrice"></td>  
     </tr>
     <tr>  
        <td>库存数量</td>  
        <td colspan="3"  id="stockCount"></td>  
     </tr>
  </table>
</div>

Core js code:

<script>

$(function(){
   getDetail();
});

function getDetail(){
   var goodsId = g_getQueryString("goodsId");
   $.ajax({
      url:"/goods/detail/"+goodsId,
      type:"GET",
      success:function(data){
         if(data.code == 0){
            render(data.data);
         }else{
            layer.msg(data.msg);
         }
      },
      error:function(){
         layer.msg("客户端请求有误");
      }
   });
}

function render(detail){
   var miaoshaStatus = detail.miaoshaStatus;
   var  remainSeconds = detail.remainSeconds;
   var goods = detail.goods;
   var user = detail.user;
   if(user){
      $("#userTip").hide();
   }
   $("#goodsName").text(goods.goodsName);
   $("#goodsImg").attr("src", goods.goodsImg);
   $("#startTime").text(new Date(goods.startDate).format("yyyy-MM-dd hh:mm:ss"));
   $("#remainSeconds").val(remainSeconds);
   $("#goodsId").val(goods.id);
   $("#goodsPrice").text(goods.goodsPrice);
   $("#miaoshaPrice").text(goods.miaoshaPrice);
   $("#stockCount").text(goods.stockCount);
   countDown(); // 判断秒杀开始状态
}

// 判断秒杀开始状态
function countDown(){
    var remainSeconds = $("#remainSeconds").val();
    var timeout;
    if(remainSeconds > 0){//秒杀还没开始,倒计时
       $("#buyButton").attr("disabled", true);
       $("#miaoshaTip").html("秒杀倒计时:"+remainSeconds+"秒");
        timeout = setTimeout(function(){
            $("#countDown").text(remainSeconds - 1);
            $("#remainSeconds").val(remainSeconds - 1);
            countDown();
        },1000);
    }else if(remainSeconds == 0){//秒杀进行中
        $("#buyButton").attr("disabled", false);
        if(timeout){
            clearTimeout(timeout);
        }
        $("#miaoshaTip").html("秒杀进行中");
        $("#verifyCodeImg").attr("src", "/miaosha/verifyCode?goodsId="+$("#goodsId").val());
        $("#verifyCodeImg").show();
        $("#verifyCode").show();
    }else{//秒杀已经结束
        $("#buyButton").attr("disabled", true);
        $("#miaoshaTip").html("秒杀已经结束");
        $("#verifyCodeImg").hide();
        $("#verifyCode").hide();
    }
}

</script>


3, remember that the server returns json format data, static pages to do data binding.




Third, the client rendering (spike Interface)

And operating the second point is basically the same, but also remove the dynamic template language instead ajax rendering.
Different places:
1), a few more springboot configuration;
2) the difference between GET and POST, POST must use here, there are some scenes, such as deletions, if used such wording such as GET delete id = XX,? then the search engine will automatically scan to help you remove, so be sure to write clearly type.

1, springboot-1.5.x configuration

spring.resources.add-mappings=true #是否启用默认资源处理
spring.resources.cache-period= 3600 #缓存时间
spring.resources.chain.cache=true #是否在资源链中启用缓存
spring.resources.chain.enabled=true #是否启用Spring资源处理链。默认情况下,禁用,除非至少启用了一个策略。
spring.resources.chain.gzipped=true #是否对缓存压缩
spring.resources.chain.html-application-cache=true #是否启用HTML5应用程序缓存清单重写
spring.resources.static-locations=classpath:/static/ #静态资源的位置


2, the official configuration springboot2.1.1

spring.resources.add-mappings=true # 是否启用默认资源处理
spring.resources.cache.cachecontrol.cache-private= # 表示响应消息是针对单个用户的,不能由共享缓存存储。
spring.resources.cache.cachecontrol.cache-public= # 表示任何缓存都可以存储响应
spring.resources.cache.cachecontrol.max-age= # 响应被缓存的最大时间,如果没有指定持续时间后缀,以秒为单位。
spring.resources.cache.cachecontrol.must-revalidate= # 表明,一旦缓存过期,在未与服务器重新验证之前,缓存不能使用响应。
spring.resources.cache.cachecontrol.no-cache= # 表示缓存的响应只有在服务器重新验证时才能重用
spring.resources.cache.cachecontrol.no-store= # 表示在任何情况下都不缓存响应
spring.resources.cache.cachecontrol.no-transform= # 指示中介(缓存和其他)它们不应该转换响应内容
spring.resources.cache.cachecontrol.proxy-revalidate= # 与“must-revalidate”指令的含义相同,只是它不适用于私有缓存。
spring.resources.cache.cachecontrol.s-max-age= # 响应被共享缓存缓存的最大时间,如果没有指定持续时间后缀,以秒为单位。
spring.resources.cache.cachecontrol.stale-if-error= # 当遇到错误时,响应可能使用的最大时间,如果没有指定持续时间后缀,以秒为单位。
spring.resources.cache.cachecontrol.stale-while-revalidate= # 如果没有指定持续时间后缀,则响应在过期后可以提供的最长时间(以秒为单位)。
spring.resources.cache.period= # 资源处理程序提供的资源的缓存周期。如果没有指定持续时间后缀,将使用秒。
spring.resources.chain.cache=true # 是否在资源链中启用缓存。
spring.resources.chain.compressed=false # 是否启用已压缩资源(gzip, brotli)的解析。
spring.resources.chain.enabled= # 是否启用Spring资源处理链。默认情况下,禁用,除非至少启用了一个策略。
spring.resources.chain.html-application-cache=false # 是否启用HTML5应用缓存清单重写。
spring.resources.chain.strategy.content.enabled=false # 是否启用内容版本策略。
spring.resources.chain.strategy.content.paths=/** # 应用于内容版本策略的以逗号分隔的模式列表。
spring.resources.chain.strategy.fixed.enabled=false # 是否启用固定版本策略。
spring.resources.chain.strategy.fixed.paths=/** # 用于固定版本策略的以逗号分隔的模式列表。
spring.resources.chain.strategy.fixed.version= # 用于固定版本策略的版本字符串。
spring.resources.static-locations=classpath:/META-INF/resources/,classpath:/resources/,classpath:/static/,classpath:/public/ # 静态资源的位置。


3, the second click point <Now spike> JS method call button getMiaoshaPath () as follows

<script>

function getMiaoshaPath(){
    var goodsId = $("#goodsId").val();
    g_showLoading();
    $.ajax({
        url:"/miaosha/path",
        type:"GET",
        data:{
            goodsId:goodsId,
            verifyCode:$("#verifyCode").val()
        },
        success:function(data){
            if(data.code == 0){
                var path = data.data;
                doMiaosha(path);
            }else{
                layer.msg(data.msg);
            }
        },
        error:function(){
            layer.msg("客户端请求有误");
        }
    });
}

function doMiaosha(path){
    $.ajax({
        url:"/miaosha/"+path+"/do_miaosha",
        type:"POST",
        data:{
            goodsId:$("#goodsId").val()
        },
        success:function(data){
            if(data.code == 0){
                //window.location.href="/order_detail.htm?orderId="+data.data.id;
                getMiaoshaResult($("#goodsId").val());
            }else{
                layer.msg(data.msg);
            }
        },
        error:function(){
            layer.msg("客户端请求有误");
        }
    });
    
}

function getMiaoshaResult(goodsId){
    g_showLoading();
    $.ajax({
        url:"/miaosha/result",
        type:"GET",
        data:{
            goodsId:$("#goodsId").val(),
        },
        success:function(data){
            if(data.code == 0){
                var result = data.data;
                if(result < 0){
                    layer.msg("对不起,秒杀失败");
                }else if(result == 0){//继续轮询
                    setTimeout(function(){
                        getMiaoshaResult(goodsId);
                    }, 200);
                }else{
                    layer.confirm("恭喜你,秒杀成功!查看订单?", {btn:["确定","取消"]},
                            function(){
                                window.location.href="/order_detail.htm?orderId="+result;
                            },
                            function(){
                                layer.closeAll();
                            });
                }
            }else{
                layer.msg(data.msg);
            }
        },
        error:function(){
            layer.msg("客户端请求有误");
        }
    });
}

</script>




Fourth, the object cache

The most commonly used basic caching logic:

  1. Failure: An application to start taking data cache, do not get, fetch data from the database, after the success, into the cache.
  2. Hit: application access to data from the cache, taken after return.
  3. Update: put data into the database, after the success, let cache invalidation.


    Reference Code:
// 先查缓存,再查数据库。
public MiaoshaUser getById(long id) {
   //取缓存
   MiaoshaUser user = redisService.get(MiaoshaUserKey.getById, ""+id, MiaoshaUser.class);
   if(user != null) {
      return user;
   }
   //取数据库
   user = miaoshaUserDao.getById(id);
   if(user != null) {
      redisService.set(MiaoshaUserKey.getById, ""+id, user);
   }
   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);
   }
   //更新数据库
   MiaoshaUser toBeUpdate = new MiaoshaUser();
   toBeUpdate.setId(id);
   toBeUpdate.setPassword(MD5Util.formPassToDBPass(formPass, user.getSalt()));
   miaoshaUserDao.update(toBeUpdate);
   //处理缓存
   redisService.delete(MiaoshaUserKey.getById, ""+id);
   user.setPassword(toBeUpdate.getPassword());
   redisService.set(MiaoshaUserKey.token, token, user);
   return true;
}




to sum up

  • Server rendering: generate static html pages to a specified directory or redis such as caching middleware to use template engine technology, access path on the page pointing directly to static html in the directory, this approach actually still belongs to the server to render the static way.
  • The client rendering: the original template language to render html, change is purely static htm / shtml, then js / ajax rendering data, plus configuration spring configuration file to specify a static file storage path, to use browser cache page The way. This is recommended, which belongs to the front and rear end of the separation of client rendering, better performance.
  • Object Caching: object-level cache is mainly caching some of the objects in the service, we should follow reasonable steps to fetch buffer, and then take the database, the cache has returned cached objects, no return database data, and finally the database data again placed in the cache.


    If you are interested caching logic, you can refer to this blog: http: //blog.csdn.net/tTU1EvLDeLFq5btqiK/article/details/78693323


Guess you like

Origin www.cnblogs.com/fulongyuanjushi/p/11367038.html