2008-11-23
Обсудить на форуме
Принять участие в разработке библиотеки/утилиты можно на GitHub.
Библиотека HTTP_StaticMerger 2.x позволяет "на лету" сливать набор
статических файлов (CSS и JS) и, таким образом, ускорять загрузку страницы,
экономя HTTP-запросы. Ближайший аналог
minify, имеющий более расширенный
функционал, но зато большую сложность и размер.
Естественно, библиотеку рекомендуется применять совместно с кэширующим
reverse proxy (например, nginx) для минимизации времени ответа. Возможности:
- Обработка директив @import и удаление комментариев из кода, что экономит трафик.
- "Слитый" URL зависит от времени модификации входящих в него файлов.
- Поддержка заголовков HTTP If-Modified-Since, If-None-Match, Etag.
- Работа под очень высокой нагрузкой (при использовании reverse-proxy сервера с модулем кэширования, например, nginx).
- В версии 2.x поддерживается цифровое подписывание URL, защищающее от "зафлуживания" файловой системы злоумышленником в случае использования nginx.
- Компрессия слитого URL для сокращения его длины.
Вы можете сразу посмотреть примеры использования.
|
Владельцы нагруженных проектов! Прочтите, пожалуйста, раздел Nginx: gzip-сжатие и кэширование
в нижней части страницы, чтобы узнать, как правильно использовать HTTP_StaticMerger под нагрузкой.
|
Суть проблемы
В сложных сайтах количество подключаемых CSS- и JS-файлов может быть достаточно большим,
из-за чего скорость загрузки страницы сильно страдает:
Листинг 1: подгрузка нескольких статических файлов
|
<head>
<script type="text/javascript" src="/js/jquery.js"></script>
<script type="text/javascript" src="/js/jquery-dimensions.js"></script>
<script type="text/javascript" src="/js/left-menu.js"></script>
...
</head> |
Загрузка каждого файла порождает отдельный HTTP-запрос, и браузер не может показать
содержимое страницы до тех пор, пока не загрузятся все до единого CSS- и JS-файлы. Естественно, это
замедляет отображение.
Библиотека HTTP_StaticMerger позволяет использовать единый URL для загрузки сразу
нескольких CSS- или JS-файлов:
Листинг 2: примеры запросов на слияние
|
<head>
<script src="/merge.php/!!a2de6253!!12345678!!js/jquery.js!!js/jquery-dimensions.js!!..."></script>
<link rel="stylesheet" type="text/css" href="/merge.php/!!3ad3b1f8!!12345678!!css/common.css!!css/menu.css!!..."></script>
</head> |
Как пользоваться: простейший вариант
Для начала положите в DOCUMENT_ROOT файл merge.php примерно следующего содержания:
Листинг 3: /merge.php обработчик для слияния
|
<?php
require_once "../lib/HTTP/StaticMerger.php";
$merger = new HTTP_StaticMerger('a-secret-and-constant-string');
$merger->main('windows-1251'); // Желательно указать кодировку файлов. |
Затем в коде, формирующем HTML-страницу, перечислите CSS- или JS-файлы, которые вы планируете "слить" вместе:
Листинг 4: вставка URL для слияния файлов
|
<head>
<?
$merger = new HTTP_StaticMerger('a-secret-and-constant-string');
echo $merger->getHtml("/merge.php/", array("js/jquery.js", "js/jquery-dimensions.js", ...));
?>
</head> |
В целях отладки иногда бывает удобно временно отключить слияние файлов. Для этого передайте третий
параметр в метод getHtml(), равный true. Тогда каждый файл будет представлен отдельным
тэгом <script> или <link>:
Листинг 5: третий параметр true слияния не происходит
|
echo $merger->getHtml("/merge.php/", array("js/jquery.js", ...), !empty($_GET['no_merge'])); |
В общем-то, это и есть необходимый минимум.
Nginx: gzip-сжатие и кэширование
Если вы планируете внедрять HTTP_StaticMerger в сильно нагруженном проекте,
пожалуйста, не пытайтесь сделать это "в лоб". Дело в том, что процедура слияния файлов
происходит при каждом HTTP-запросе и занимает ощутимое время (средняя машина выдает
примерно 100-200 запросов слияния в секунду).
Чтобы разгрузить сервер, используйте кэширующий reverse proxy перед веб-сервером
(например, nginx). Пусть он кэширует "навечно" все запросы к merge-скрипту:
Модификация обработчика слияний
Листинг 6: /merge.php обработчик для слияния с gzip-сжатием
|
<?php
require_once "../lib/HTTP/StaticMerger.php";
ob_start();
$merger = new HTTP_StaticMerger('a-secret-and-constant-string');
$merger->main('windows-1251');
$c = ob_get_clean();
if ($c && empty($_SERVER['HTTP_NOGZIP'])) ob_start(array('ob_gzhandler', 9));
echo $c; |
|
Обратите внимание: мы включаем gzip-сжатие только в случае, если nginx это не запретил (переменная HTTP_NOGZIP
устанавливается в конфигурации nginx в зависимости от браузера, см. ниже). Также запрещаем gzip-сжатие для
ответов с "пустым" телом (например, "304 Not Modified"), иначе возникают проблемы как минимум в Google Chrome.
|
Создание кэш-директории
Листинг 7: Для начала создайте директорию кэша.
|
mkdir -p /var/cache/nginx
chown nginx:nginx /var/cache/nginx |
Конфигурация для nginx+apache+mod_php
В случае, если вы работаете с apache+mod_php, а nginx применяется в качестве фронтенд-сервера, используйте следующие настройки:
Листинг 8: Конфигурация для nginx+apache+mod_php
|
# Включаем кэширование для сжатых файлов.
proxy_cache_path /var/cache/nginx levels= keys_zone=merge:10m;
location /merge.php {
set $no_gzip 0;
if ($http_user_agent ~ "MSIE [4-6]\.|Safari|Konqueror") {
# В этих браузерах имеются баги при обработке gzip-сжатых файлов с ДЛИННЫМИ URL-ами.
set $no_gzip 1;
}
if ($http_accept_encoding !~ "gzip") {
set $no_gzip 1;
}
proxy_pass http://127.0.0.1:80;
proxy_set_header Host $host;
# Nginx caching.
proxy_cache merge;
proxy_cache_valid 200 304 404 240h;
proxy_cache_key "$no_gzip|$request_method|$http_if_modified_since|$http_if_none_match|$host|$request_uri";
proxy_set_header NOGZIP $no_gzip;
} |
|
Внимание! В MS Internet Explorer 6.0 и младше отмечаются проблемы с кэшированием gzip-сжатых данных, если длина URI CSS- или JS-файла превышает 215 символов. Соответственно, мы выключаем в этих браузерах сжатие.
|
Или - конфигурация для FastCGI PHP
А эти настройки для поклонников FastCGI-версии PHP:
Листинг 9: Конфигурация для FastCGI-версии PHP
|
# Включаем кэширование для сжатых файлов.
fastcgi_cache_path /var/cache/nginx levels= keys_zone=merge:10m;
location /merge.php {
set $no_gzip 0;
if ($http_user_agent ~ "MSIE [4-6]\.|Safari|Konqueror") {
# В этих браузерах имеются баги при обработке gzip-сжатых файлов с длинными URL-ами.
set $no_gzip 1;
}
if ($http_accept_encoding !~ "gzip") {
set $no_gzip 1;
}
fastcgi_pass 127.0.0.1:9000;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
# Nginx caching.
fastcgi_cache merge;
fastcgi_cache_valid 200 304 404 240h;
fastcgi_cache_key "$no_gzip|$request_method|$http_if_modified_since|$http_if_none_match|$host|$request_uri";
fastcgi_param HTTP_NOGZIP $no_gzip;
} |
Использование совместно с mod_rewrite
Конечно, можно при помощи Apache mod_rewrite или nginx rewrite делать URL сливаемых файлов более аккуратными, вот такими:
/merge.php?some=param&other=!!a2de6253!!12345678!!js/jquery.js!!...
/merge/!!a2de6253!!12345678!!js/jquery.js!!...
/m/!!a2de6253!!12345678!!js/jquery.js!!...
Последний пример продразумевает, что вы использовали директиву mod_rewrite:
Листинг 10: улучшение вида URL слияния при помощи mod_rewrite
|
RewriteRule ^m/(.*) merge.php/$1 |
Если так не сработает, попробуйте вариант с "?" в итоговом URL:
Листинг 11: улучшение вида URL слияния при помощи mod_rewrite
|
RewriteRule ^m/(.*) merge.php?$1 |
|
Старайтесь, однако, избегать части "?..." в URL слияния, т.к. некоторые старые
браузеры отключают клиентское кэширование для таких адресов либо работают неоптимально.
|
|
|
|