首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

真實世界中的 Rails 開發:進階頁面緩存技術

     編輯:Java 相關知識

在 Ruby on Rails 應用開發中,頁面緩存是提升性能的終極武器。當 Rails 生成靜態 HTML 文件並交由 Web 服務器直接服務時,應用服務器完全繞過 Rails 堆疊,性能提升可達數量級。然而,現實世界的應用很少是完全靜態的——用戶登錄狀態、個性化內容、動態數據都會讓頁面緩存失效。

頁面緩存的困境

以 ChangingThePresent.org 為例,幾乎每個頁面都包含用戶相關的動態元素:登錄用戶看到「個人資料」和「登出」鏈接,未登錄用戶則看到「登錄」和「註冊」。傳統頁面緩存無法處理這種「同一 URL、不同用戶、不同內容」的場景。

登錄與登出視圖對比

頁面緩存示意圖

三種解決策略

作者提出三種應對方案:

  1. 放棄頁面緩存,改用片段緩存——最保守但失去極致性能
  2. AJAX 異步加載動態片段——頁面主體緩存,動態部分由 JavaScript 請求
  3. Cookie + JavaScript 客戶端渲染——將用戶狀態存入 Cookie,頁面完全靜態化,由瀏覽器端 JavaScript 根據 Cookie 動態顯示/隱藏內容

第三種方案最激進也最有效:Rails 完全不參與請求處理,實現真正的靜態文件服務。

Hide-and-Seek 技術實現

核心思想:在靜態頁面中同時渲染所有用戶類型的內容版本,通過 CSS 隱藏不需要的部分,JavaScript 讀取 Cookie 後顯示正確版本。

步驟 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>

關鍵技巧:`your profile` 鏈接不帶用戶 ID,指向通用的 `profiles#index`,由控制器根據當前用戶重定向到正確資料頁。

步驟 2:登錄/登出時管理 Cookie

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

def logout
  cookies.delete :login
  cookies.delete :image
  # ...
end

步驟 3:JavaScript 讀取 Cookie 控制顯示

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';
  }
}

步驟 4:頁面加載時自動執行

<script type="text/javascript">
  window.onload = function() {
    handle_cached_user();
    // 其他 onload 代碼...
  }
</script>

步驟 5:啟用頁面緩存

caches_page :index

進階:Show-and-Tell 技術處理個性化數據

當需要顯示具體用戶名、頭像等個性化數據時,結合 Hide-and-Seek 與 Show-and-Tell:先用 Hide-and-Seek 選擇正確的 partial,再用 JavaScript 將 Cookie 中的數據注入 DOM。

個性化視圖對比

Logged-in Partial 預留動態替換點

<div id='logged_in' style="display: none;">
  <%= link_to "Hi, <b id='bold_link'>my_login</b>", ... %>
  <div id='picture_and_link'>
    <a id='link_for_member_thumbnail' href="...">
      <img id='member_thumbnail' src="/images/default/def_member_thumbnail.gif" />
    </a>
  </div>
  <div id="not_mine">Not my_login?</div>
  <%= image_button "logout", ... %>
</div>

JavaScript 動態注入用戶數據

function handle_user_partial() {
  var login_cookie = readCookie('login');
  var image_cookie = readCookie('image');
  
  document.getElementById('profile_link').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;
}

整合調用

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';
  }
}

緩存失效策略

頁面緩存不會自動過期,需要定期清理。最簡單方式:定時任務刪除 `public/index.html`。對於更複雜的場景,可使用 Sweeper 觀察模型變更精確失效。

實戰成果

ChangingThePresent.org 通過這套方案:

  • 基礎時間清理器:緩存 75%+ 頁面
  • 進階清理策略:緩存 90%+ 頁面
  • 應用服務器僅處理 1%-3% 的 Web 請求

代價是增加了 HTML/JavaScript 維護複雜度,但換來的是極致的可擴展性。下一篇將探討 ActiveRecord 性能優化技巧。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved