Практическое использование Rails: Часть 2. Усовершенствованная технология кэширования страниц в России и странах СНГ [изменить] Условия использования Главная страница Продукты Услуги и решения Поддержка и загрузка Мой профиль  developerWorks РоссияДругие статьи из этой серии:Практическое использование RailsВ этой статье:“Показать и добавить” или “спрятать и найти”?Реализация метода "спрятать и найти"Реализация метода "показать и добавить"ЗаключениеРесурсыОб автореВыскажите мнение об этой страницеСсылки по теме:SOA и Web-сервисы техническая библиотека developerWorks Россия  >  SOA и Web-сервисы  >Практическое использование Rails: Часть 2. Усовершенствованная технология кэширования страницИспользование JavaScript cookie-файлов для расширения возможностей кэширования страницОпции документа Опции документа, требующие включения 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 В серии Практическое использование 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. Создание всех версий динамического контента в одном представлении
<%= link_to "login", :controller => 'members', :action => 'login' %>
<%= link_to "register", :controller => 'members', :action => 'signup' %>
Вероятно, вы обратили внимание на ссылку 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 при загрузке страницы Это простой код 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. Отображение фрагментов и для вошедших, и для не вошедших в систему пользователей
<%= render :partial => 'common/logged_in' style="display: none; %>
<%= render :partial => 'common/logged_out' %>
Следующий шаг - это контент для составляющей logged_in. Обратите внимание, что у каждого HTML-компонента, содержащего динамический контент, есть ID и, следовательно, его можно будет найти с помощью JavaScript и при необходимости заменить: Листинг 7. Показ составляющей logged_in