Статика

Материал из Eludia
Перейти к: навигация, поиск
Чтобы для вас удовольствие сделать, я же готов хотя пятнадцать раз кряду сдавать — и всё то самое буде!

Н. Щедрин, «Современная идиллия».

Содержание

Сложное WEB-приложение — это прежде всего большое количество HTML-страниц, динамически генерируемых в ответ на разнообразные запросы пользователей. Однако не стоит забывать, что каждая такая страница содержит десятки ссылок на графические изображения, таблицы стилей (CSS), библиотеки javaScript и прочие статические файлы.

Для любого WEB-серевера обслуживание статических запросов — настолько элементарная задача, что об отладке и настройке здесь зачастую не вспоминают. И зря. Грамотная работа со статикой может существенно сэкономить ресурсы сервера и заметно снизить время отклика, что весьма важно при активной работе.

Директория статики

Во всех Eludia-приложениях все статические файлы располагаются в директории /i/. Это позволяет (как будет показано ниже) гибко управлять разнообразными опциями, относящимися к обслуживанию статических запросов. У Apache при использовании mod_perl и установке обработчика perl-script на корневую директорию следует выделять статический подраздел явно:

<Location /i>
  SetHandler default
  ...
</Location>

Файлы, загружаемые на сервер, традиционно располагаются в директории /i/uploads/. Чаще всего они выдаются на клиент не напрямую, а через специальный динамический запрос, например, с использованием функции sql_download_file.

Если вы хотите обеспечить разделение прав доступа на документы, то, скорее всего, стоит сделать это средствами приложения. В таком случае доступ к /i/uploads/ следует закрыть для всех. Аналогичным образом можно защитить директорию /i/templates/, хотя, скорее всего, шаблоны печатных документов сами по себе большой тайны не составляют.

Кэширование на стороне клиента

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

Для форсированного кэширования на клиенте сервер сопровождает ответ специальными заголовками: Expire и Cache-Control. У Apache для этого предназначен модуль mod_expires (убедитесь, что он не отключён в глобальной конфигурации). Для директории статики приложения его следует активизировать следующим образом:

<Location /i>
  SetHandler default
  ExpiresActive on
  ExpiresDefault "now plus 1 days"
  ...
</Location>

Статика ядра Eludia

Кнопки, меню, элементы ввода — всё это принадлежность ядра Eludia, точнее, отдельных $_SKIN'ов. И при смене версии ядра эта статика может обновиться. Отсюда вытекают 2 проблемы:

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

Обновление версии

Все запросы на статику ядра сопровождаются номером сессии ($_REQUEST {sid}) текущего пользователя. Таким образом, при обновлении версии ядра, если на клиенте возникает, скажем, конфликт нового DHTML со старой js-библиотекой, достаточно выполнить новый вход в систему. Свежие версии статических файлов будут загружены с сервера и (если работает mod_expires) останутся там достаточно надолго.

Директории статики ядра

Каждое приложение содержит свою копию статики ядра.

На страте (при загрузке) приложения ядро Eludia создаёт у него поддиректорию /i/_skins с правом записи для WEB-сервера, а в дальнейшем каждый $_SKIN при первом использовании копирует свои статические файлы в поддиректорию, соответствующую его имени. Например $_SKIN Classic копирует *.gif, *.css и *.js в /i/_skins/Classic.

Нередко возникает необходимость переопределить часть статики $_SKIN'а непосредственно в приложении: поменять стандартных дизайн иконок (не трогая геометрию), перекрасить фоновые градиенты и т. п. Для этого следует использовать директорию-двойник: /i/skins/. Например, чтобы заменить иконку ok.gif в $_SKIN'е Classic, следует записать её в /i/skins/Classic/ok.gif.

При этом /i/skins/ является принадлежностью приложения и обязательно должна находиться в версионном репозитории, а директория /i/_skins/ является временной и ни в коем случае не может находиться в версионном репозитории.

Переопределение CSS и JS

Если каждая иконка лежит в отдельном файле, то все CSS-классы описаны в одном файле (eludia.css) и, аналогично, все js-функции — в одной библиотеке (navigation.js). Переопределять их полностью технически реально, но пользоваться этой возможностью не следует, поскольку в этом случае как раз нарушится целостность версий ядра.

Дописывать оригинальные CSS и JS следует непосредственно в директорию /i/. Чтобы подключить эти файлы к динамической странице приложения, следует указать их в качестве специальных параметров запроса:

$_REQUEST {__include_css} = ['my']; # /i/my.css
$_REQUEST {__include_js}  = ['my']; # /i/my.js

Отдельный сервер для статики

Из соображений оптимизации следует всеми способами минимизировать число "тяжёлых" серверов, одновременно занимающих место в памяти. Поскольку запросы на статику, с одной стороны, идут весьма неравномерно (один динамический запрос может породить десяток статических), а с другой — никак не используют интерпретатор Perl или PHP, лучше всего вообще вынести картинки и библиотеки на отдельный хост, обслуживаемый специальным лёгким WEB-сервером.

При этом идеально, если только вы можете себе это позволить, развести динамическую и статическую часть приложения по портам (например, динамика на стандартном 80-м порту, статика — на 8000-м). Иначе, скорее всего, придётся использовать реверсное проксирование, усложняющее функционирование системы и блокирующее использование некоторых возможностей (в частности, Keep-Alive соединений).

Адрес хоста статики прописывается в httpd.conf приложения:

use Eludia::Loader
...
static_site => 'http://my.application.ru:8000',
...

Если этот адрес зависит от запроса (он должен различаться для пользователей офисной сети и внешней Internet), то в качестве значения можно задать функцию:

static_site => sub { 		
 my ($host, $port) = split /\:/, $ENV {HTTP_HOST};
 $port =~ s{^8}{9};
 return "http://${host}:${port}";
},

В результате почти все ссылки на статику будут изменены соответствующим образом.

Досадные исключения

Подчас кажется, что вот он у тебя в руках, простой и элегантный способ глобальной оптимизации — ан нет. Натыкаешься на требования так называемой "безопасности" — и пропала вся красота. Ну или не вся, а частично.

Итак, безопасность. MSIE содержит встроенную защиту от "вредоносных сайтов", которая подчас срабатывает без предупреждения и не поддаётся настройке. Одним из таких случаев является использование статики с чужого хоста в скриптах. Замечено минимум 2 проблемы:

  1. showModalDialog не передаёт dialogArguments диалогу, открытому с чужого хоста;
  2. во фрейме, открытом с чужого хоста, не удаётся дистанционно запускать скрипты.

По этой причине некоторые статические URL в Eludia-интерфейсах не могут использовать опцию static_site, и, соответственно, избавиться от выдачи статики "тяжёлым" сервером путём настройки отдельного хоста полностью не удаётся.

А если вам необходимо использовать HTTPS — то вообще из этой затеи ничего не выйдет. Один хост, один порт — и баста. В этом случае придётся настраивать лёгкий proxy.

Настройка nginx

nginx — отличный WEB-сервер, оптимизированный под высокую загрузку. Если вы решите использовать его для статических запросов единственного Eludia-приложения, то всё, что потребуется — вписать в его конфигурационный файл примерно следующее:

server { 
 listen 8000; 
 location / {
  root /var/projects/my_application/docroot;
  expires 30d;
 }
}

Обычно на тестовых и рабочих серверах одновременно настроены несколько (иногда десятки) приложений. Как правило, их их директории содержатся в одной-единственной, например, /var/projects. В этом случае удобно сконфигурировать nginx так, чтобы он обеспечивал статику сразу для всех приложений без дополнительной подстройки:

server { 
 listen 8000;
 root /var/projects;
 location / {
  expires 30d;
  rewrite ^(.*)/i/(.*)  /$1/docroot/i/$2 break;
  return 403;
 }
}

В этом случае URL статики будут иметь вид http://some.host:8000/my_application/i/..., соответственно в локальные httpd.conf надо будет вписывать строчки

static_site => 'http://some.host:8000/my_application',

Несмотря на то, что в root попадают и библиотеки, и конфигурация, по HTTP будут доступны только файлы из поддиректорий /i/.

И ещё совет: отключите у nginx логгирование совсем:

   access_log off;

Настройка lighttpd

lighttpd — ещё один высокопроизводительный WEB-сервер. Если вы решите использовать его исключительно для статики в Eludia-приложениях, имеет смысл сделать с конфигурационным файлом следующее (имеется в виду, что вы установили конфигурацию по умолчанию):

  1. отключить все модули, кроме mod_expire;
  2. закомментировать accesslog.filename и server.errorlog;
  3. прописать как порт по умолчанию фиктивный, неиспользуемый номер (чтобы не конфликтовал с основным приложением, которое наверняка занимает станндартный 80-й), например 9999;
  4. для каждого виртуального хоста добавить раздел вида
$SERVER ["socket"] == "my.application.ru:8000" {
 server.document-root = "/var/projects/my_application/docroot/"
 expire.url = ("" => "access 1 months")
}

Лёгкий proxy

Другая стратегия оптимизации состоит в том, чтобы Apache / mod_perl отдавал HTTP-ответы не напрямую, а через реверсный HTTP proxy сервер, обладающий способностью самостоятельно обслуживать запросы на статику. В качестве такового может использоваться:

  • Apache без mod_perl, но с mod_proxy или его более совершенными аналогами, например, mod_accel;
  • nginx;
  • lighttpd

и другие.

При настройке достаточно добиться того, чтобы запросы на директорию /i обслуживались прокси-сервером, а все прочие передавались Apache / mod_perl. В Eludia-приложении при этом не требуется перенастраивать ничего.

Такой подход имеет несколько недостатков:

  • по определению невозможен режим Keep-Alive, соответственно, неработоспособна NTLM-авторизация;
  • промежуточная буферизация может нарушить функционирование системы в случае долгих ответов с предусмотренной последовательной обработкой на клиенте.

Вообще схема настройки приложения с лёгким реверсным proxy чрезвычайно схожа с использованием FastCGI, только в последнем случае перечисленные проблемы решаются сами собой. Зато на горизонте появляется другая: поскольку FastCGI процессы изолированы, об использовании разделяемой памяти не может быть и речи (правда, под Windows в этом смысле терять нечего, поскольку там copy-on-write недоступен никому). Так или иначе, Eludia-приложения могут работать с использованием FastCGI в комбинации с тремя WEB-серверами: lighttpd, IIS и nginx (только с использованием внешнего контейнера).

В заключение приведём пример конфигурации nginx для случая, когда проксирование оптимально:

  • доступ к серверу извне осуществляется по HTTPS;
  • операционная система — UNIX/Linux.
   server {

       listen       443;

       ssl                  on;
       ssl_certificate      /var/projects/my_application/conf/server.crt;
       ssl_certificate_key  /var/projects/my_application/conf/server.key;

       root                 /var/projects/my_application/docroot;

       location /i/ {
           expires 30d;
       }

       location = /favicon.ico {
           proxy_pass       http://127.0.0.1;
       }

       location = / {
           proxy_pass       http://127.0.0.1;
           proxy_set_header X-Forwarded-For $remote_addr;
           proxy_buffering  off;
       }

       location / {
           return 403;
       }

   }
Персональные инструменты
Пространства имён

Варианты
Действия
Навигация
Разработчику
Администратору
Инструменты