среда, 8 февраля 2012 г.

Почему нужно минимизировать память web-приложения

Все, что изложено ниже, является вольным пересказом некоторых интересных примечаний из книги M. Dashorst, E. Hillenius "Wicket in Action".

Есть одно важное отличие в плане разработки между традиционным "тонким" клиентом и современным полнофункциональным web-приложением, о котором не так часто говорят. Это отличие - в том, что при разработке web-приложения нужно стремиться к минимуму используемой им памяти.

Представим для начала, что для работы с сервером вам требуется написать просто desktop-приложение. Будете ли вы в первую очередь беспокоиться о том, сколько памяти оно будет требовать для своей работы? Учитывая, что дни компьютеров с 640 KB на борту давно позади, и мы живем в те времена, когда для того, чтобы купить несколько тысяч лишних MB оперативы, не нужно копить несколько лет, работая на трех работах, даже если ваше приложение будет требовать для работы 10-20 MB памяти, это значит лишь, что вам просто не о чем волноваться. Не так уж и плохо, учитывая, что iTunes может запросто скушать 145 MB.

Но если вы пишете web-приложение, которым будут пользоваться 100 человек одновременно, то тогда требования к памяти сервера взлетают сразу же до 1 GB! А что будет, если ваше приложение станет популярным и о нем узнают тысячи человек? В таком случае у вас будет два выхода (вариант "бросить нафиг это гиблое дело" мы пока что рассматривать не будем), первый из которых совсем не исключает второго: придется вложить кучу денег в оборудование и/или найти путь снизить требования приложения к памяти.

На самом деле, хранение большого количества данных в пользовательской сессии на сервере само по себе не очень хорошая затея. Пользовательские сессии содержат в себе по сути копии объектов данных, и эти копии обычно быстро становятся устаревшими. Так, если некий пользователь Вася случайно нажмет кнопку "Назад" в браузере, то он может быть немного озадачен: "Что произошло? Я же уже вводил здесь информацию о своем любимом коте!". И еще, чем больше пользователей, тем больше копий одного и того же объекта будет храниться в памяти. А это пустая трата памяти, которая могла бы быть использована для других данных и пользователей.

Кроме того, если приложение выполняется на кластере, то чрезмерное раздувание пользовательских сессий может сказаться на производительности. Дело в том, что в случае выполнения на кластере состояние сессии должно быть синхронизировано между всеми узлами кластера. И чем больше это состояние, тем больше информации должно быть синхронизировано. Синхронизация стоит времени чтения из канала и записи в канал (по-научному это называется операциями ввода-вывода) и ширины этого самого канала. И также, обычно, синхронизация производится с использованием - не поверите! - сериализации. А это явно не может сказаться лучшим образом на производительности. Поэтому уменьшение количества данных, которые требуется синхронизировать, - обычно самый верный способ избавиться от тормозов в такого рода приложениях.

В принципе, альтернативой хранению данных в клиентской сессии может быть получение данных каждый раз по запросу клиента. Но этот способ тоже чреват проблемами с производительностью.

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

среда, 1 февраля 2012 г.

Wicket: Установка режима совместимости Internet Explorer

В 8-й версии всеми нами “любимого” ослобраузера Internet Explorer (далее просто IE) появилась одна очень интересная фишечка. Назвали ее режимом совместимости. Смысл ее состоял в том, чтобы позволить пользователю просматривать web-страницы таким образом, как будто они отображаются в одной из предыдущих версий IE (так, в IE8 предполагался режим совместимости только с IE7).

Как это закономерно случается со всеми хорошими начинаниями в IE, фича со временем стала предметом еще одного геморроя для web-разработчиков.

Поясню. Во-первых, количество режимов совместимости с выходом каждой новой версии браузера увеличивается (радует, пока что еще только в арифметической прогрессии). В 10-й версии браузера этих режимов будет уже туева хуча штук:

Во-вторых, если специально не говорить IE, в каком из режимов нужно отображать страницу, то он отображает страницу, как попало. Точнее, выбор режима отображения страницы зависит от большого числа не вполне контролируемых факторов, но выглядит это действительно так: как попало. :)

Для того, чтобы сообщить IE режим отображения страницы, используется заголовок ответа “X-UA-Compatible”. Детально все возможные его значения я рассматривать не буду. Об этом можно почитать, например, здесь.

Мы же поговорим о том, какие имеются возможности задания режима отображения страниц в рамках популярного web-фреймворка Apache Wicket.

Вариант первый

На самом деле, установить режим отображения web-страницы в IE можно, всего лишь задав специальный заголовок META на странице. То есть, если мы хотим, например, чтобы IE показывал web-страницу всегда в режиме номера своей текущей версии, то нам достаточно вставить строку

<meta http-equiv="X-UA-Compatible" content="IE=Edge" />


в разметку (html-файл), соответствующую классу страницы Wicket.

Проблем тут две и они следующие:

  • Данная строка всегда должна идти сразу после открывающего тега <head> (пробелы тоже нежелательны! :)), иначе IE ее тупо проигнорирует. Это с некоторой долей вероятности может привести к проблемам при добавлении в коде страницы различных header contributor’ов и т.д.
  • Данный заголовок нужно будет указывать отдельно для каждой из страниц проекта, если, например, в вашем приложении используется несколько шаблонных страниц (или шаблонные страницы не используются вообще).

Вариант второй

Добавить заголовок META для всех страниц приложения можно с помощью следующей строчки кода в методе init класса приложения (потомка WebApplication):

public class MyApp extends WebApplication {
  ...
  @Override
  protected void init() {
    ...
    addRenderHeadListener(new StringHeaderContributor(
        "<meta http-equiv=\"X-UA-Compatible\" content=\"IE=Edge\" />\n"));    
  }
  ...
}

Если эта строчка будет последним вызовом метода addRenderHeadListener в методе init, то необходимый нам заголовок с большой долей вероятности будет первой строкой в разделе <head /> на страницах приложения.

Но опять же не всегда.

Вариант третий

Чтобы указать необходимые нам параметры совместимости непосредственно в заголовке ответа для всех web-страниц, переопределим метод newRequestCycle класса приложения:

public class MyApp extends WebApplication {
  ...
  @Override
  public RequestCycle newRequestCycle(Request request, Response response) {
    return new WebRequestCycle(this, (WebRequest)request, (WebResponse)response) {

      @Override
      public WebResponse getWebResponse() {
          WebResponse response = super.getWebResponse();
          response.setHeader("X-UA-Compatible", "IE=Edge");
          return response;
      }
    }
  }
  ...
}

При этом нужно помнить о том, что указанный на странице заголовок META имеет приоритет над  HTTP-заголовком, который передается в response.

Такие дела. Наверняка, имеются и другие способы управлять режимом совместимости в IE средствами Wicket, но тратить слишком много времени на борьбу с вIEтренными мельницами не очень хочется.

Пойду я лучше готовиться к Хэллоуину. :) Начать никогда не рано, я считаю.