zoukankan      html  css  js  c++  java
  • 大量并发的应急处理方案与实践2——使用缓存(转)

    本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/Dreamcode/archive/2010/05/26/5624429.aspx

     

    大量并发的应急处理方案与实践2——使用缓存
        《大量并发的应急处理方案与实践》提供的方法,只能做应急时的一种参考,不推荐在设计之初时使用,设计之初我们应该采用更合理的架构,以避免文中所提及的问题发生,关于架构请参考我的另一篇文章《开餐馆与做软件——如何提高大型网站性能》。
          资源可以分成两种,一种为禁止并发访问的资源,另一种为允许并发访问的资源。禁止并发访问的资源如高速公路收费站,每一个收费口一次只处理一个通过请求,当有多个车辆请求通过时,解决的办法有两种,一种是增设收费口,另一种就是让车辆排队等候,即不马上处理请求,让请求等待空闲资源,我把这种解决办法称作后向异步处理,即将需要处理的请求,延后一段时间处理,这种方法适用于用户不需要看到即使效果的情况。关于“后向异步处理” 的例子请参考《大量并发的应急处理方案与实践1——异步处理》。允许并发访问的资源如商场,任何顾客都可以随时购买需要的商品,早先的大部分商场商品前都有一个柜台,顾客通过售货员购买需要的商品,当顾客增多后,售货员就会应接不暇,让顾客排队是一个办法,但是没有哪个商场会强制顾客排队购买商品,这种“后向异步处理”不适合解决这样的问题,于是一种更好的解决方案产生了——“超市”,取出部分商品直接摆放在售货台上,让顾客自己去取,然后只需要安排一个服务人员随时观察,发现商品不全再到库房中取来相应商品进行补充。
          即将如下流程:
     顾客 -> 售货员1 -> 商品1 -> 付费
     顾客 -> 售货员2 -> 商品2 -> 付费
     顾客 -> 售货员3 -> 商品3 -> 付费
          改造为
     库房 -> 服务员 —> 售货台(缓存)(前向异步处理)
     顾客 -> 商品(1、2、3)->  排队付费(后向异步处理)
          这是一个典型例子,顾客对于付费的请求不需要即时响应,我们可以采用后向异步处理的方法。但是对于查看商品的请求越早满足越好,因此我们采用另一种前向异步处理的办法,将商品(数据)提前放到售货台(缓存)里,这样当顾客(请求)需要的时候可以直接取走。下面我们来看一个你可能会遇到的例子:
          有一天你的老板突然接到电话,客户抱怨说当登录人数增加的时候,页面打开的非常慢,这时老板找到你,你竭尽全力向老板解释,老板用无奈的眼神望着你,对你说:"用户很急,全靠你了。"
          你垂头丧气的走回到座位上,开始一点点排查原因,这时你发现原来这些页面在打开时,伴随大量的数据库操作,
    这就是页面打开速度慢的原因。遇到这样的问题,比较简单快速的改造方法就是使用缓存。
          即是将如下结构,
     用户-> 页面与逻辑 -> 数据库
          改造为:
     数据库 -> 监控程序 -> 缓存 (前向异步处理)
     用户-> 页面与逻辑 -> 缓存
     

          举例来说你发现需要加速的页面 test.jsp, 其通过TestDB.java 类获取数据。

        public class TestDB{
                     ...
                    // 读取数据库 1
                    String sql1 = "..."; //查询语句
                    List tmplist1 = queryBySQL(sql1); //queryBySQL 方法,执行sql 语句,执行结果放到一个List 中返回
                    ...
                    // 读取数据库 2
                    String sql2 = "..."; //查询语句
                    List tmplist2 = queryBySQL(sql2); //queryBySQL 方法,执行sql 语句,执行结果放到一个List 中返回
                    ...
                    // 读取数据库 3
                    String sql3 = "..."; //查询语句
                    List tmplist3 = queryBySQL(sql3); //queryBySQL 方法,执行sql 语句,执行结果放到一个List 中返回
                    ...
        }

     

           首先,我们创建一个与页面相对应的缓存模块(例如Buffer)。为需要缓存数据的页面创建一个与之对应的类TestBuffer.java 用来存放数据。必须保证TestBuffer.java 中的数据与数据库中相应数据的一一对应关系,因此我们将TestBuffer.java 设计为一个单例。

        #Buffer
               -- TestBuffer.java

     

         public class TestBuffer{
                   private volatile static TestBuffer singleton=null;
                   private TestBuffer(){}

                   public static TestBuffer getInstance()
                  {

                         if(singleton==null){
                               synchronized(TestBuffer.class)
                              {
                                      singleton=new TestBuffer();
                               }
                         }
       
                         return singleton;
                  }

         }

     

        我们分析需要加速页面的数据库操作模块,找出所有数据库操作点。例如我们分析TestDB.java ,发现如下数据库
    操作点:读取数据库 1、读取数据库 2、读取数据库 3。这三个操作点分别返回三个List 对象,这三个List 对象就是我们要缓存的数据。我们将这三个对象加入到testBuffer.java 中。

         public class TestBuffer{
                   private volatile static TestBuffer singleton=null;
                   private TestBuffer(){}

                   private static List tmplist1 = null
                   private static List tmplist2 = null
                   private static List tmplist3 = null

     

                   public static TestBuffer getInstance()
                   {

                           if(singleton==null){
                                  synchronized(TestBuffer.class)
                                  {
                                         singleton=new TestBuffer();
                                  }
                           }
       
                           return singleton;
                    }


                    public List getTmplist1(){
                          return tmplist1;
                    }
     
                    public List setTmplist1(List tmplist1){
                          this.tmplist1 = tmplist1;
                    }

                    public List getTmplist2(){
                          return tmplist2;
                    }
     
                    public List setTmplist2(List tmplist2){
                          this.tmplist2 = tmplist2;
                    }

                    public List getTmplist3(){
                          return tmplist3;
                    }
     
                    public List setTmplist3(List tmplist3){
                         this.tmplist3 = tmplist3;
                    }

          }

     

       重新改造原先的数据库访问模块TestDB.java ,更改如下: 
       
        public class TestDB{
                 ...
               //String sql1 = "..."; //查询语句
               //String sql2 = "..."; //查询语句
               //String sql3 = "..."; //查询语句
               //List tmplist1 = queryBySQL(sql1); //queryBySQL 方法,执行sql 语句,执行结果放到一个List 中返回
               //List tmplist2 = queryBySQL(sql2); //queryBySQL 方法,执行sql 语句,执行结果放到一个List 中返回
               //List tmplist3 = queryBySQL(sql3); //queryBySQL 方法,执行sql 语句,执行结果放到一个List 中返回
     
               List tmplist1 = TestBuffer.getTmplist1();
               List tmplist2 = TestBuffer.getTmplist2();
               List tmplist3 = TestBuffer.getTmplist3();

                ...

        }

     

        然后我们添加从数据库到缓存的前向异步处理模块,即我们实现一个提前运行的监控程序,将数据库中的数据放到缓存中,并保持缓存与数据库中的数据同步,我们可以使用线程实现。

     public class testThread implements Runnable{

               private static long interval = 3000; //循环间隔

               @Override
               public void run(){
                     while(true){
                            ...

                           String sql1 = "..."; //查询语句
                           String sql2 = "..."; //查询语句
                           String sql3 = "..."; //查询语句
                           List tmplist1 = queryBySQL(sql1);
                           TestBuffer.setTmplist1(tmplist1);

     

                           List tmplist2 = queryBySQL(sql2); 
                           TestBuffer.setTmplist2(tmplist2);

                           List tmplist3 = queryBySQL(sql3);
                           TestBuffer.setTmplist3(tmplist3);
                            ...

                           try { 
                                  Thread.sleep(interval);
                           } catch (InterruptedException e) {
                                  // TODO Auto-generated catch block
                                 e.printStackTrace();
                           }

                    }

             }

     }

        以上提供的方法只做您遇到类似问题时的一种参考,是术,方法千变万化而道理不离其宗,合理使用缓存和异步处理是解决这一类问题的核心。

  • 相关阅读:
    Docker-常用命令
    5分钟了解折半插入排序
    Spring框架之IOC原理
    使用JS实现简单喷泉效果
    坦克大战系列6-API常用函数说明1
    坦克大战系列6-API常用函数说明2
    为什么要使用-Docker
    SQL语言:存储过程
    使用原生JS重构简单的音乐播放器
    [区间DP]ZOJ3541 The Last Puzzle
  • 原文地址:https://www.cnblogs.com/jjj250/p/2628716.html
Copyright © 2011-2022 走看看