IBM®
Перейти к тексту
    в России и странах СНГ [изменить]    Условия использования
 
 
   
    Главная страница    Продукты    Услуги и решения    Поддержка и загрузка    Мой профиль    
Перейти к тексту

developerWorks Россия  >  SOA и Web-сервисы  >

Практическое использование Rails: Часть 2. Усовершенствованная технология кэширования страниц

Использование JavaScript cookie-файлов для расширения возможностей кэширования страниц

developerWorks
Опции документа

Опции документа, требующие включения JavaScript, не отображаются

Обсудить


Выскажите мнение об этой странице

Помогите нам улучшить содержание


Уровень сложности: средний

Брюс Тэйт, президент, RapidRed

13.03.2008

Обычно, если контент зависит от пользователя, то все усилия по кэшированию страниц сводятся на нет: контент, предоставляемый одному пользователю, слегка отличается от контента, предоставляемого другому. Кэширование страниц можно применять, используя JavaScript совместно с cookie-файлами, даже если на страницах отображаются какие-либо пользовательские данные. В данной статье будет проведен анализ усовершенствованной технологии кэширования страниц в Ruby on Rails.

Как уже говорилось, Rails практически никогда не участвует в процессе кэширования страниц. В принципе, это очень удобно, так как в результате значительно повышается производительность. Rails создает HTML-страницу лишь единожды, затем помещает ее в каталог и напрочь забывает о ней. С этого момента сервер приложений обслуживает эти страницы без использования цикла, постоянно обращающегося к серверу приложений. С точки зрения производительности применение кэширования страниц становится настоящим подарком.

Мне нравится идея кэширования страниц, а Rails делает его простым и понятным. Активировать кэширование можно с помощью всего лишь одной строчки программного кода. А если добавить еще несколько строчек, то можно очистить кэш простым удалением файла или применением Rails API высокого уровня. Но тут возникает следующая проблема: кэширование страниц можно применить не на каждом сайте. Если на странице есть информация, изменяющаяся в зависимости от того, кто ее просматривает, то кэширование всей страницы использовать нельзя. А если возникают сложности с определением срока истечения актуальности страницы, то кэширование страниц может оказаться непростой задачей.

Например, практически на каждой странице сайта ChangingThePresent.org (см. боковую панель) присутствуют некоторые данные пользователя, которые изменяются в зависимости от того, какой пользователь авторизовался на сайте. На рисунке 1 показан один из разделов нашей домашней страницы. (Мы все еще пытаемся организовать ее правильно, поэтому есть вероятность, что она будет изменена.) Особых проблем с функционированием этой страницы нет: если есть возможность определить, что пользователь вошел в систему, то отображение можно настроить в реальном времени при помощи Flash, JavaScript, DHTML или какого-либо другого кода на основе браузера. Очевидно, что авторизованный пользователь может завершить сеанс или загрузить свой профиль, а неавторизованный - пройти процедуру регистрации или войти в систему.


Рисунок 1. Представления для вошедших и не вошедших в систему пользователей ChangingThePresent.org
Представления для вошедших и не вошедших в систему пользователей ChangingThePresent.org

В серии Практическое использование Rails всемирно известный автор и оратор Брюс Тэйт покажет реальную картину разработки Rails изнутри. Будучи главным техническим директором в WellGood, LLC, он отвечает за проектирование, разработку и поддержку ChangingThePresent.org - портала благотворительных пожертвований, где можно пожертвовать деньги на оплату часа работы в области исследований рака, на защиту акра тропического леса или проспонсировать операцию по устранению катаракты и вернуть зрение слепому. К настоящему времени сотни тысяч пользователей получили бесплатную помощь на ChangingThePresent, поэтому популярность и масштабы сайта постоянно растут.

Существуют десятки статей, описывающих процесс создания простых приложений Rails. Данная серия выведет вас за рамки основ построения простого блога и расскажет о вопросах, которые приходится решать каждому сайту, созданному на базе Rails. Вы узнаете, как оптимизировать Rails и сделать работу сайта более стабильной, как при помощи плагинов можно обойти основные ограничения Rails и что нужно сделать, чтобы сайты на основе Rails работали в реальной жизни.

На рисунке 2 показано несколько изменённое отображение данных пользователя, которое и используется на нашем сайте. Два варианта, показанные на рисунке, кардинально отличаются друг от друга. Для того чтобы управлять кэшированием страниц, необходимо учитывать все их отличия. Для каждого вошедшего в систему пользователя необходимо заменить часть страницы, отображающую информацию для пользователей, не выполнивших вход в систему, на ту ее часть, которая отображает идентификатор авторизованного пользователя и его фотографию. Кэширование данной части контента представляет собой еще одну проблему, так как данные всех пользователей различны.


Рисунок 2. Два различных представления
Два различных представления

Подобный режим работы характерен отнюдь не только для сайта ChangingThePresent.org. Персонализация пользовательского интерфейса ведет к сокращению числа случаев использования немодифицированного кэширования страниц Rails. Однако если использовать небольшое количество настроек, можно без особых проблем обеспечить кэширование этих страниц.

Все вышеописанные проблемы можно решить различными способами. Наиболее продуктивными из них с моей точки зрения можно считать следующие:

  • Использование кэширования фрагментов вместо кэширования страниц (оставаясь в рамках ограничений среды Rails)
  • Загрузка большей части страницы, а затем - использование JavaScript и Ajax для загрузки небольшой динамической части. Выполняемый на сервере код будет определять, вошел ли пользователь в систему, а затем воспроизводить необходимую часть с помощью Ajax.
  • Сохранение определенного количества данных о пользователе и его действиях. Например, сохранение в cookie-файле на стороне клиента информации о входе пользователя в систему. Затем можно динамически изменить внешний вид страницы при помощи JavaScript в зависимости от содержимого cookie-файла.

Из этих трех способов наиболее предпочтительным является третий, так как первый и второй затрагивают Rails-приложение. Если нужна максимальная масштабируемость, предпочтительно иметь дело с максимально статичным контентом. В данной статье основное внимание будет сосредоточено на третьем способе. Не применяйте данную технологию для хранения важной информации, утрата которой критична, например, для кодов запуска ICBM или номеров кредитных карт. Данное решение идеально для ограниченного набора данных.

“Показать и добавить” или “спрятать и найти”?

Когда я начал экспериментировать с кэшированием домашней страницы, я мог бы просто заменять ссылки при помощи JavaScript. Назовем эту технологию "показать и добавить". На основе той информации, которая известна об авторизованном пользователе, можно показать пользователю корректную информацию, выборочно добавив или заменив части Web-страницы при помощи JavaScript. Для реализации данной технологии необходимо сделать следующее:

  • Создать Web-страницу и включить в нее только те элементы, которые являются общими для всех пользователей.
  • Если пользователь вошел в систему, поместить некоторую информацию о нем (например, имя пользователя) в cookie-файл.
  • Оставшуюся часть страницы заполнить HTML-фрагментами, созданными при помощи JavaScript на основе содержимого cookie-файла.

Что касается домашней страницы ChangingThePresent, технология "показать и добавить" являлась бы явным излишеством, так как нужно отображать только два набора ссылок, учитывая данные о том, вошел ли пользователь в систему. Предпочтение было отдано второй технологии - "спрятать и найти". Итак, покажем все элементы страницы, которые являются общими для всех пользователей, и создадим скрытую версию каждого варианта данных для персонализированных элементов. Это часть нашего метода, которую мы назвали “спрятать”. Затем на основе роли пользователя можно использовать JavaScript для нахождения данных элементов в документе. Это часть “найти”. Может возникнуть мысль, что показ всех возможных вариантов данных является громоздким, однако ситуация, в которой необходимо выборочно активировать различные функции для ролей с различными уровнями безопасности, является достаточно типичной. Методика "спрятать и найти" является идеальной для домашней страницы ChangingThePresent. Для реализации данной технологии, необходимо выполнить следующее:

  • Создать Web-страницу и включить в нее только те элементы, которые являются общими для всех пользователей.
  • Разделить всех пользователей на группы. Добавить варианты контента для каждой группы пользователей. В нашем случае для домашней страницы ChangingThePresent в качестве групп выступают только два типа пользователей: вошедшие в систему, и не сделавшие этого. Изначально следует сделать этот контент невидимым.
  • При входе пользователя в систему поместите информацию, характеризующую ту ли иную группу пользователей (например, роль пользователя или его статус), в cookie-файл.
  • При обращении пользователя к странице, выборочно покажите ту версию контента, которая предназначена для данной группы пользователей.

Реализация метода "спрятать и найти"

Для домашней страницы ChangingThePresent, реализация технологии "спрятать и найти" элементарна. Напомним, что домашняя страница содержит фрагмент, изображенный на рисунке 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. Изначально эта ссылка указывала на профиль пользователя, однако это помешает кэшированию нашей домашней страницы. Вместо этого я просто направил ссылку на действие index, без указания ID пользователя. А затем действие index перенаправляет пользователя на корректную страницу профиля:


Листинг 2. Перенаправление пользователя на корректную страницу профиля
                
    def index
        redirect_to my_profile_url
    end

В листинге 2 метод my_profile_url определяет URL соответствующего профиля на основе типа пользователя, который может быть “знаменитостью”, “советником” или “рядовым участником”. У каждого есть отдельная страница профиля. На данном этапе приложение полностью работоспособно, однако вы увидите все четыре ссылки: по две ссылки для вошедших в систему (logged_in) и не вошедших (logged_out):

  • login
  • register
  • your profile
  • logout

Следующий шаг заключается в создании cookie-файла, в котором будет храниться текущий тип пользователя. Для ChangingThePresent cookie-файл создается во время входа в систему и хранит текущий ID входа в систему. Cookie-файл будет удален в момент выхода из системы:


Листинг 3. Создание и удаление cookie-файлов при входе и выходе из системы
                
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. Код 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';
    }
}

Первая функция считывает значение cookie-файла из JavaScript, а вторая работает с 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. Метод "спрятать и найти" отлично работает для тех страниц, у которых есть несколько классификаций пользователей, но не будет работать для отображения данных каждого отдельного пользователя, как показано на рисунке 2. В этом случае, придется использовать сочетание методов "спрятать и найти" и "показать и добавить".

Реализация метода "показать и добавить"

Взгляните на рисунок 2 еще раз. Методика "спрятать и найти" будет использоваться для выбора правильного варианта фрагмента в зависимости от того, вошел ли пользователь в систему или нет, а затем будет применяться технология "показать и добавить" для заполнения динамических фрагментов страницы на основе содержимого cookie-файлов, создание которых описано в листинге 3, строки четыре и пять. Подчеркиваю, что при использовании технологии "показать и добавить" я изменяю элементы страницы так, чтобы они соответствовали каждому отдельно взятому пользователю.

Во-первых, у нас имеется статический контент, который отображает каждую составляющую: одна для пользователя, который не выполнил вход в систему, другая - для вошедшего в систему пользователя. По умолчанию предполагается, что пользователь не вошел в систему, поэтому раздел logged_in скрыт при помощи стилевого форматирования display: none. Впоследствии при необходимости их можно скрывать или показывать при помощи кода JavaScript. Обратите внимание, что для определения каждого раздела используются те же самые имена logged_in и logged_out, поэтому вносить какие-либо изменения в JavaScript, написанный для домашней страницы, не придется:


Листинг 6. Отображение фрагментов и для вошедших, и для не вошедших в систему пользователей
                
<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. Обратите внимание, что у каждого HTML-компонента, содержащего динамический контент, есть ID и, следовательно, его можно будет найти с помощью JavaScript и при необходимости заменить:


Листинг 7. Показ составляющей logged_in
                
<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, то вероятно обратили внимание на несколько пользовательских функций помощи. Можно увидеть 4 различных элемента динамического контента, которые необходимо заменять при каждой загрузке страницы с помощью JavaScript: информация о входе в систему (в трех местах) и изображение участника (в одном месте). Код JavaScript состоит из изменений функции handle_cached_user и нового метода для управления обновлением страницы для динамического пользователя. При написании данной статьи код был слегка упрощен. Добавьте в файл application.js следующую функцию:


Листинг 8. Замена элементов пользовательской составляющей
                
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 сначала считывает cookie-файлы и получает один элемент дерева DOM: ссылку на профиль текущего пользователя, которая называется profile_link. Затем функция handle_user_partial:

  • заменяет имя вошедшего в систему пользователя (которое хранится в login_cookie) на my_login для создания правильного URL страницы профиля пользователя.
  • добавляет имя вошедшего в систему пользователя в DOM-элемент, который содержит текст, написанный жирным шрифтом и обозначающий вошедшего в систему пользователя.
  • добавляет простое предложение "Not login?" в DOM-элемент, содержащий подпись logout в разделе login.
  • находит DOM-элемент, в котором хранится изображение участника и заменяет URL универсального изображения на URL изображения конкретного участника, который хранится в image_cookie.
  • а также заменяет тег alt для изображения на имя пользователя (login), на тот случай, если рисунок не будет отображен.

При перемещении по DOM вы обнаружите, что возникают такие ситуации, когда необходимо перейти непосредственно к некоторому DOM-элементу, а иногда необходим конкретный потомок этого элемента, например, когда вы работаете с текстом. Для поиска первого потомка заданного DOM-элемента, я использовал функцию firstChild. Библиотека Prototype благодаря более простому синтаксису слегка упрощает работу с конкретными DOM-элементами, однако это выходит за рамки данной статьи.

Так как все cookie-файлы уже созданы, последним шагом является вызов JavaScript из существующей функции handle_cached_user. Напоминаю, что эта функция располагается в 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'; 
    }
}


Обратите внимание на дополнительную строчку в handle_cached_user в условии else. Эта строка выполнит необходимые подстановки перед тем, как отобразить все DOM-элементы раздела logged_in. Теперь для кэширования целых страниц осталось только воспользоваться директивами кэширования, о которых шла речь в данной статье и статье, опубликованной в прошлом месяце.

Заключение

Эта передовая технология открыла перед нами множество возможностей. Что касается ChangingThePresent.org, то по нашим оценкам мы сможем кэшировать более 75% наших страниц с использованием очень простых функций очистки, работающих по времени. При использовании чуть более сложных технологий очистки можно будет кэшировать более 90% обращений к нашим страницам или даже больше. А при учете наших агрессивных планов по кэшированию изображений, количество обращений к серверу приложений будет составлять только от 1% до 3% всех Web-запросов.

Не забывайте и об обратной стороне медали. Система стала значительно сложнее. Следовательно, придется поддерживать более сложный HTML-код и проверять синхронизацию кодов HTML и JavaScript. Однако явным преимуществом описанного подхода является возможность повышения производительности на основе использования простого и эффективного механизма кэширования. Данный подход можно опробовать, зайдя на ChangingThePresent.org и загрузив домашнюю страницу. Затем загрузите все меню верхнего уровня. Вы обнаружите, что кэшированию подлежат 4 из 6 пунктов меню верхнего уровня. Создайте учетную запись и загрузите все меню заново. Сможете угадать, какие страницы кэшируются? В следующей статье речь пойдёт о технологиях, которые помогут повысить производительность ActiveRecord, и мы продолжим изучение практического использования Rails.



Ресурсы

Научиться
  • Оригинал статьи: Real world Rails, Part 2: Advanced page caching (EN).

  • Все статьи серии Практическое использование Rails .

  • Библиотека JavaScript Prototype: библиотека, упрощающая навигацию по HTML DOM-деревьям.(EN)

  • От Java к Ruby: что должен знать ваш менеджер (Pragmatic Bookshelf, 2006): книга автора этой статьи о том, когда и где имеет смысл перейти от языка программирования Java к Ruby on Rails и как это сделать.(EN)

  • Changing The Present: благотворительный сайт, на котором можно отдать в дар акр тропического леса, помочь вернуть зрение слепому или пожертвовать час времени на исследование рака. Этот сайт положен в основу данной серии статей.(EN)

  • Улучшенное кэширование страниц с Ruby on Rails: в данной статье рассказывается о более сложных технологиях кэширования страниц и рассматривается JavaScript, который является основой для кода, реализующего чтение cookie-файлов Rails. В статье приведено подробное описание методов обеспечения безопасности на основе ролей и их связь с кэшированием страниц. (EN)

Получить продукты и технологии
  • Ruby on Rails: загрузите Web-среду Ruby on Rails с открытыми исходным кодом.(EN)


Обсудить


Об авторе

Брюс Тэйт (Bruce Tate) является отцом, горным байкером и байдарочником, проживающим в Austin, Texas. Он автор трех бестселлеров по языку Java, в том числе, победителя Jolt "Лучше, быстрее, легче Java". Недавно издал "За пределами Java"… Он работал 13 лет в IBM, а сейчас является основателем RapidRed consultancy, где специализируется на стратегиях и архитектурах облегченной разработки, основанных на технологии Java и Ruby on Rails.




Выскажите мнение об этой странице


Пожалуйста, найдите минутку и заполните форму, чтобы повысить уровень сервиса.



ДаНетНе знаю
 


 


12345
 


Поделиться этой статьей:

забобрить забобрить memori сохранить в memori




В начало


IBM обладает всеми авторскими правами касательно информации, расположенной на developerWorks. Использование информации приведенной на этом ресурсе без явного письменного разрешения от IBM или первоначального автора запрещены. Если Вы желаете использовать информацию с developerWorks, пожалуйста воспользуйтесь регистрационной формой для того, чтобы связаться с нами запрос на использование материалов developerWorks Россия.

    IBM в России Конфиденциальность Контакты