• 企业400电话
  • 网络优化推广
  • AI电话机器人
  • 呼叫中心
  • 全 部 栏 目

    网站建设 商标✡知产 微网小程序 电商运营 彩铃•短信 增值拓展业务
    举例理解Ruby on Rails的页面缓存机制
    POST TIME:2021-10-18 15:25

    有了页面缓存,Rails 就可以不再介入。在某种程度上,这是件好事,因为您的确可以获得优秀的性能。Rails 只需创建 HTML 页面,将其放入目录,之后,就可以置之于脑后。从那时起,就由应用服务器管理这些页面,且页面进入应用服务器无需任何循环。从性能的角度而言,页面缓存真是天赐之福。

    我也钟爱页面缓存,Rails 使之简单利落。只需使用一行代码就可以启用缓存。如果再加入一些代码,就能通过简单地删除文件操作或使用 Rails 较高层的 API 终止缓存。这里存在一个问题。并不是每个网站都能使用页面缓存。如果页面上的数据会根据访问它的用户而改变,那么就不能进行页面缓存。而且,如果很难判断页面何时到期终止,就会发现页面缓存的要求太过苛刻。

    比如,几乎在每个页面上,ChangingThePresent.org(参阅 侧栏)都有某些用户数据是根据当前登录的用户而变化的。图 1 显示了我们最新主页的一部分。(我们一直在努力完善它,所以它有可能会改变。)这个页面呈现出的问题相对简单。如果能判断用户是否已经登录,就可以用 Flash、JavaScript、DHTML 或任何其他基于浏览器的代码动态定制视图。您会发现已登录的用户可以登出系统或查看其配置文件,而已登出的用户则可以注册或再次登录。
    图 1. ChangingThePresent.org 上的登录和登出视图

    图 2 显示了稍微有些高级的用户数据视图,我们的站点就使用了这个视图。图 2 中的两个视图有极大的不同。为了处理页面缓存,我必须先解决所有的差异。对于每个已登录的用户,我都必须替换掉页面的登出内容,使之显示登录用户的登录 ID 和用户图片。缓存这些内容会带来另一层面的挑战,因为每个用户的数据都不尽相同。
    图 2. 两个截然不同的视图

    这种情况并非 ChangingThePresent.org 所独有。如果需要个性化用户体验,那么不可修改的 Rails 页面缓存的使用就会受到限制。但如果定制不多,那么实际上还是能很容易地缓存这些页面的。

    解决这些问题的方法很多。我更倾向于使用如下这些技巧:

    在这三种技巧中,我更喜欢第三种,因为第一和第二种技巧都会将 Rails 应用程序牵扯进来。要获得最大限度的可伸缩性,就要尽量多地使用静态内容。在本文中,我会侧重于介绍第三种方式。请不要使用该方法存储任何不能丢失的敏感数据,比如 ICBM 启动代码或信用卡号。对于我们所处理的这些有限的数据而言,这种方法效果很好。
    使用 Show and tell 还是 hide and seek?

    在我刚开始试着缓存这个主页时,我本可以简单地用 JavaScript 替换这些链接。可以将这种技巧看成是 Show-and-tell。基于我们对已登录用户的了解,可以使用 JavaScript 选择性地替换或注入 Web 页的部分内容,从而为用户提供正确的体验。为了进一步细分,我会进行如下操作:

    对于 ChangingThePresent 主页而言,show-and-tell 技巧有些威力过度,因为我只有两套链接要根据所登录的用户加以显示。因此,我选择了第二种技巧,我称之为 hide-and-seek。首先,显示出所有用户的公共页面元素,并通过每种数据可能 的隐藏版本显示页面的变化部分。这就是 hide 部分。然后,根据用户的角色使用 JavaScript 在文件中找到该用户的内容并显示出来。这就是 seek 部分。您可能会想,显示所有可能数据的版本有点威力过度,实际上,选择性地为不同的安全角色启用多种特性时,这种方式是十分常见的。hide-and-seek 方式非常适合 ChangingThePresent 主页。要实现这种方法,可以执行如下操作:

    实现 hide and seek

    对于 ChangingThePresent 主页而言,hide-and-seek 实现起来异常简单。在之前的图 1 中,此主页有一个部分显示的是与用户帐户相关的一些链接。这些链接可以根据用户是否登录而变化。首要工作是构建此页的所有公共内容。我在本文并未给出具体做法。第二页需要显示出所有用户的全部动态内容,而不管用户是否已经登录:
    清单 1. 在单一视图中创建动态内容的所有版本

    div id='logged_out'>
     %= link_to "login", :controller => 'members', :action => 'login' %>
     br />
     %= link_to "register", :controller => 'members', :action => 'signup' %>
    /div>
    div id='logged_in' style="display: none;">
     %= link_to "your profile", :controller => 'profiles', :action => 'show' %>
     %= link_to "logout" , :controller => "members", :action => "logout" %>
    /div>
    
    

    您可能已经注意到 my profile 链接。起初,该链接指向用户特定的配置文件,但这样可能会妨碍我们的主页缓存。相反,我只简单地将此链接指向了无任何用户 ID 的索引操作。然后,索引操作会将用户重定向到正确的配置文件页:
    清单 2. 将用户重定向到正确的配置文件页

      def index
        redirect_to my_profile_url
      end
    
    

    在清单 2,my_profile_url 是一个方法,该方法可以根据用户的类型(这可能是名人、顾问或会员)决定正确的配置文件 URL。每个用户类型都有一个单独的配置文件页。这时,程序的功能已经完成,您总共可以看到四个链接,logged_in 和 logged_out 各有两个链接:

    下一步,获取含有当前用户类型的 cookie。对于 ChangingThePresent,我在登录时创建了一个 cookie,其中含有当前的登录 ID。之后,在登出时再销毁这个 cookie:
    清单 3. 在登录和登出时创建和销毁 cookies

    def login
     if request.post?
      self.current_user = User.authenticate(params['user_login'], params['user_password'])
      ...
    
      if logged_in?
       set_cookies
       ...
      end
    end
    
    def logout
    end
    
    private
    
    def set_cookies
     cookies[:login] = current_user.login
     cookies[:image] = find_thumb(current_user.member_image)
    end
    
    def logout
     cookies.delete :login
     cookies.delete :image
     ...
    
    end
    
    

    在清单 3 中,logged_in? 是一个私有方法,如果当前用户已登录则返回 true。上述的 Rails 方法会在您登录时创建三个 cookie,并在登出时删除它们。这里不需要为数据费神。尚且不需用到数据。可以这样理解:无需调用 Rails 框架,我就可以判断用户是否登录。我无需确保 cookie 到期终止是否与站点的到期终止规定相符。在我的例子中,二者是相符的,所以我现在尽可以开始页面缓存了。

    下一步,根据用户的 cookie 选择性地隐藏和显示正确的条目。将如下的 JavaScript 代码添加到 public/javascripts/application.js 中:
    清单 4. 支持 show and hide 登录 div 的 JavaScript 代码

    function readCookie(name) {
      var nameEQ = name + "=";
      var ca = document.cookie.split(';');
      for(var i=0;i  ca.length;i++) {
        var c = ca[i];
        while (c.charAt(0)==' ') c = c.substring(1,c.length);
        if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
      }
      return null;
    }
    
    function handle_cached_user() {
     var login_cookie = readCookie('login');
      var logged_in = document.getElementById('logged_in');
      var logged_out = document.getElementById('logged_out');
      if(login_cookie == null) {
       logged_in.style.display = 'none';
       logged_out.style.display = 'block';
      } else {
       logged_out.style.display = 'none';
       logged_in.style.display = 'block';
      }
    }
    
    

    第一个函数从 Javascript 中读取 cookie 值,第二个函数处理此 DOM。可以通过使用 Prototype 库简化这段代码,但我包括了基本的 DOM 查找以便于读者理解。最后一步是在页面加载时调用 JavaScript 函数。我向布局中添加了如下代码:
    清单 5. 当页面加载时调用 JavaScript 函数

      script type="text/javascript">
       window.onload = function() {
         handle_cached_user();
    
         %= render_nifty_corners_javascript %>
          %= yield :javascript_window_onload %>
       }
      /script>
    
    

    上述 JavaScript 代码十分简单。在页面加载时,将加载 handle_cached_user 函数,而它会相应地显示或隐藏正确的内容。现在,我尽可以通过向控制器中添加如下代码来启用页面缓存:

      caches_page :index
    
    

    上述代码效果极佳。我还是需要定期地从缓存中删除前页,这样我才能使该页期满终止。为此,我只需简单地定期删除 public/index.html。hide-and-seek 方式对于有几类用户的页面十分有效,但对于如图 2 中所示的用户 partial 效果却不佳。对于后者,需要综合使用 hide-and-seek 和 show-and-tell 技巧。
    实现 show-and-tell

    再来看看 图 2。我将使用 hide-and-seek — 根据用户是否已登录 — 选择 partial 的正确版本,然后使用 show-and-tell 技巧根据我之前在清单 3 的行 4 和行 5 中所写的 cookies 的内容填充页面的动态部分。请记住,对于 show-and-tell,我特别更改了页面的元素以符合单个用户的情况。

    首先,完成在这两个 partial (即登出用户和登录用户)上呈现的静态内容。我假设用户已经登出,所以我会通过附加 display: none 风格隐藏 logged_in div。之后,如果必要,我就可以用 JavaScript 显示或隐藏它们。请注意,我使用了相同的两个名称:logged_in 和 logged_out,来识别每个 div,这样便无需对我为这个主页所编写的 JavaScript 进行修改:
    清单 6. 呈现登录和登出这两个 partial

    div class="boxRight sideColumnColor">
      div id='logged_in'>
        %= render :partial => 'common/logged_in' style="display: none; %>
      /div>
      div id='logged_out'>
        %= render :partial => 'common/logged_out' %>
      /div>
    /div>
    
    

    接下来,完成 logged_in partial 的内容。注意,每个包含动态内容的 HTML 组件都有一个 ID,从而我可以使用 JavaScript 找到它并随后将其替换:
    清单 7. 显示 logged_in partial

    div id='logged_in' style="display: none;">
    
     %= link_to %(span class="mainBodyDark">Hi, /span>) +
        %(span class="textLarge mainBodyDark">b id='bold_link'>) + "my_login" +
        %(/b>/span>), {:controller => 'profiles', :action => 'show', :id => 'my_login'},
     {:id => 'profile_link'} %>
     br/>
    
     div id='picture_and_link'>
       a href="http://member/my_login" id='link_for_member_thumbnail'>
         img id='member_thumbnail'
            alt="Def_member_thumbnail"
            src="/images/default/def_member_thumbnail.gif" />/a>
     /div>
    
     div id="not_mine">Not my_login?/div>
     br/>
     %= image_button "logout", :controller => "members", :action => "logout" %>
    
    

    如果对 Rails 有足够的了解,您可能会注意到其中的几个定制帮助程序函数。从中可以看到四处很明显的动态内容,我需要使用 JavaScript 为每个加载的页面替换这些内容:三处登录,一处会员图像。此处的 JavaScript 代码对 handle_cached_user 函数作了一处修改,并且还含有一个为动态用户处理页面更新的方法。针对本文的具体情况,我稍微对这段代码做了少许简化。可以将如下函数添加到 application.js 文件中:
    清单 8. 替换用户 partial 的元素

    function handle_user_partial() {
     var login_cookie = readCookie('login');
     var image_cookie = readCookie('image');
    
      var profileLink = document.getElementById('profile_link');
      profileLink.href = '/member/' + login_cookie;
    
      document.getElementById('bold_link').firstChild.nodeValue=login_cookie;
    
      document.getElementById('not_mine').firstChild.nodeValue="Not " + login_cookie + "?";
    
      document.getElementById('link_for_member_thumbnail').href="/member/" + login_cookie;
    
      document.getElementById('member_thumbnail').src=image_cookie.replace(/%2[Ff]/g,"/");
    
      document.getElementById('member_thumbnail').alt=login_cookie;
    
    }
    
    

    在清单 8 中,这个 JavaScript 函数首先读取此 cookies 并获取 DOM 树的一部分:即到当前的用户配置文件的链接,称为 profile_link。然后是 handle_user_partial 函数:

    在 DOM 中导航时,会发现有时需要直接转到 DOM 元素,而有时又需要转到该元素的特定子元素,比如在处理文本的时候。我就使用了 firstChild 函数根据需要寻找 DOM 元素的第一个子元素。由于语法更为友好,所以 Prototype 库使处理特定的 DOM 元素较为容易一些,但这超出了本文的讨论范围。

    我已经创建好了所有的 cookies,最后一步就是从 handle_cached_user 函数调用 JavaScript。请记住,该函数在 public/javascripts/application.js中:
    清单 9. 将 handle_user_partial 函数添加到 handle_cached_user

    function handle_cached_user() {
     var login_cookie = readCookie('login');
      var logged_in = document.getElementById('logged_in');
      var logged_out = document.getElementById('logged_out');
      if(login_cookie == null) {
       logged_in.style.display = 'none';
       logged_out.style.display = 'block';
      } else {
      handle_user_partial();
       logged_out.style.display = 'none';
       logged_in.style.display = 'block';
      }
    }
    
    

    请注意,else 条件中的 handle_cached_user 函数下面还有额外两行代码。这两行代码可以在使 logged_in DOM 元素可见之前进行适当的替代。剩下所需做的就是使用本篇文章和 上个月 的那篇文章中所介绍的页面缓存指令来缓存整个页。
    结束语

    本篇文章中介绍的这种高级技巧为我们打开了许多方便之门。在 ChangingThePresent.org 上,我们估计使用非常简单的基于时间的清除器能够缓存超过 75% 的页面。通过使用稍微有些复杂的清除技巧,我们就能缓存超过 90% 的页面,而且还可能更多。如果您想试图影响我们的图像缓存计划,那么您只能触及应用服务器 1% 到 3% 的 Web 请求。

    但同时,我们也应该看到不利的一面。我向此系统添加了明显的复杂性。我必须维护更加复杂的 HTML 代码,并确保 HTML 和 JavaScript 能够保持同步。但好的一面是在需要获得更好的性能时,我就能够使用最为简单和有效的缓存技术。您也可以尝试使用这种技巧 — 访问 ChangingThePresent.org 并加载主页。接下来,加载每个顶端的菜单。您会发现我们会页面缓存六个顶端菜单中的四个。创建一个帐号并重载每一个菜单。您能猜到哪个页面被缓存了么?在下一篇文章中,在继续深入真实世界中的 Rails 的同时,我将带您探究能增进 ActiveRecord 性能的一些技巧。

    您可能感兴趣的文章:
    • ruby on rails 代码技巧
    • 在阿里云 (aliyun) 服务器上搭建Ruby On Rails环境
    • Windows下Ruby on Rails开发环境安装配置图文教程
    • win7安装ruby on rails开发环境
    • 在Docker中自动化部署Ruby on Rails的教程
    • 详解Ruby on Rails中的Cucumber使用
    • 对优化Ruby on Rails性能的一些办法的探究
    • Ruby on Rails基础之新建项目
    上一篇:在Ruby on Rails中优化ActiveRecord的方法
    下一篇:在Ruby on Rails中使用Rails Active Resource的教程
  • 相关文章
  • 

    关于我们 | 付款方式 | 荣誉资质 | 业务提交 | 代理合作


    © 2016-2020 巨人网络通讯

    时间:9:00-21:00 (节假日不休)

    地址:江苏信息产业基地11号楼四层

    《增值电信业务经营许可证》 苏B2-20120278

    X

    截屏,微信识别二维码

    微信号:veteran88

    (点击微信号复制,添加好友)

     打开微信