大量并发的应急处理方案与实践2——使用缓存

大量并发的应急处理方案与实践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();
                       }

                }

         }

 }

 

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


      


 

©️2020 CSDN 皮肤主题: 酷酷鲨 设计师:CSDN官方博客 返回首页