Этот раздел написан под впечатлением статьи Viktar Karpach YSlow and ASP.NET: 100 points "A"
(рис 8.1) Измерения скорости загрузки сайта при помощи YSlow. Источник: www.karpach.comВо-первых, нужно объединить в один файл таблицы стилей и файлы скриптов. Это стоит сделать только для рабочего (не тестового) выпуска сайта при помощи MS BuiLd:
<ItemGroup> <TextFiles Include="*.css" Exclude="global.css"/> </ItemGroup> <Exec Command="echo y| type %(TextFiles.Identity) >> global.css"/>
Таким образом мы можем объединить все CSS- и JS-файлы. Некоторые разработчики зададут резонный вопрос: а что можно сказать по поводу WebResource.axd? В новом AJAX ControL TooLkit есть TooLkitScriptManager, который позволяет объединить большинство ваших файлов WebResource.axd. Также он может применять к ним gzip-сжатие (это будет важно для следующих разделов).
Затем нам нужно создать CSS
Сети доставки содержания (англ.
Можно также применить msbuild для изменения файлов стилей при
использовании
<Import Project=".\References\MSBuild.Community.Tasks.targets" /> <Target Name="Release"> <FileUpdate Files="$(OutputPath)styles\basic.css" Regex="\.\.\/images/(["\)]*)" ReplacementText= "http:=""//karpach.appspot.com/cdn/images/$1" /> </Target>
Теперь нужно положить в
Однако, по всей видимости, этот подход может быть неприменим
для файлов стилей. Для начала стоит обеспечить контроль изменений
версий таблиц стилей (в данном случае это
Движок Google Apps позволяет легко реализовывать
- url: /cdn/styles/basic_\d*\.css static_files: cdn/styles/basic.css upload: cdn/styles/basic\.css
Более подробно о настройке
У всех файлов, расположенных в
private readonly static string[] CACHED_FILE_TYPES = new
string[] { ".jpg", ".gif", ".png",".css" };
public void Init(HttpApplication context)
{
context.AcquireRequestState += new
EventHandler(context_AcquireRequestState);
}
void context_AcquireRequestState(object sender, EventArgs e)
{
HttpContext context = HttpContext.Current;
if (context != null context.Response != null)
{
string fileExtension = Path.GetExtension
(context.Request.PhysicalPath).ToLower();
if (context.Response.Cache != null
Array.BinarySearch<string>(CACHED_FILE_TYPES, fileExtension)
>= 0)
{
HttpCachePolicy cache = context.Response.Cache;
TimeSpan duration = TimeSpan.FromDays(365);
cache.SetCacheability(HttpCacheability.Public);
cache.SetExpires(DateTime.Now.Add(duration));
cache.SetValidUntilExpires(true);
cache.SetNoServerCaching();
cache.SetMaxAge(duration);
}
}
}
В принципе, у вас вообще не должно быть этого модуля с учетом то-
го, что все статические файлы должны быть расположены в
Это делается очень просто. Просто надо ввести эти действия в привычку. Помните об этом при разработке модулей на стороне сервера и используйте коллекцию Header.Controls для добавления таблиц стилей на страницу.
Следовать этому правилу бывает очень сложно. Например, иногда хочется добавить
Library.js
может содержать
function DoSomething()
{
}
А после этого прямо в коде страницы:
<script type='text/javascript'>
if (typeof (DoSomething) == 'undefined')
{
alert('Library is not loaded yet');
}
</script>
Приведенный пример будет недостаточно верным, потому что загружаемая функция может быть доступна на странице после некоторого времени. Лучше всего создавать дополнительную клиентскую логику, использующую определенный модуль, в самом этом модуле либо проверять через равные промежутки времени, доступен ли необходимый функционал. Более подробно данные техники были освещены в лекции "Ненавязчивый JavaScript" книги "Разгони свой сайт".
Для этого стоит скопировать внешние ресурсы (картинки, JavaScript файлы) к себе на сайт.
Например, на блоге располагается иконка валидной страницы от W3.Изначально эта иконка находилась на сайте W3 Schools, но для ускорения загрузки сайта стоит скопировать ее в свой проект. Тогда она будет располагаться локально, а при загрузке сайта понадобится на 1 DNS-запрос меньше.
Минимизацию JS-кода можно осуществлять при публикации очередного выпуска сайта при помощи MS Build (это наиболее разумная позиция — осуществлять все оптимизационные процедуры при превращении сайта из
тестового в рабочий). Для этой цели можно использовать YUI
<Target Name="Compress"> <Message Text="Create temp files ..." /> <Copy SourceFiles=".\$(ProjectName)\Javascript\ColorPicker.js" DestinationFiles=".\$(ProjectName)\Javascript\ColorPicker.js.full"/> <Copy SourceFiles=".\$(ProjectName)\Styles\ColorPicker.css" DestinationFiles=".\$(ProjectName)\Styles\ColorPicker.css.full"/> <Exec Command="java -jar yuicompressor-2.4.2.jar —type js .\$(ProjectName)\Javascript\ColorPicker.js.full >.\$(ProjectName)\Javascript\ColorPicker.js"/> <Exec Command="java -jar yuicompressor-2.4.2.jar —type css .\$(ProjectName)\Styles\ColorPicker.css.full >.\$(ProjectName)\Styles\ColorPicker.css"/> </Target>
Аккуратно подходите к разработке своего сервера и пользовательских расширений. Проверяйте, что несколько различных расширений загружают общие библиотеки только один раз.
Кэширующий модуль и ETag:
Response.Cache.SetETag
Данный раздел подготовлен при помощи Елены Цаплиной (
Елена известна своей публикацией о
В данный момент под ее руководством (на базе студии Aquanther) разрабатывается мультимедийный курс по созданию и управлению сайтом с использованием
DrupaL — довольная распространенная
При создании сайта используем DrupaL версии 6, так как в нем лучше реализованы внутренние средства кэширования. Также в дополнительных модулях (Views, PaneL и т. д.) для DrupaL версии 6 внедрены эффективные методы кэширования. К сожалению не все модули DrupaL версии 5 реализованы для DrupaL версии 6 (например, модуль Sphinx), о чем не следует забывать при планировании разработки Интернет-сайта. Далее будем рассматривать только DrupaL версии 6.
Хорошо обдумаем варианты использования модулей наподобие
Вариант второй, с использованием
Кэширование системы меню, фильтров форматов ввода, переменных администрирования (например, название сайта) и настроек модуля производится автоматически. Остальные параметры кэширования можно настроить на странице "Управление — Производительность" ( http://www.exampLe.ru/admin/settings/performance )
На данной странице можно настроить:
Включим кэширование страниц в режим "нормальный". В данном режиме кэширования страниц при просмотре страницы в первый раз (анонимным, незарегистрированным в системе пользователем) производится сохранение сгенерированной страницы в кэш. В дальнейшем при просмотре данной страницы (анонимным пользователем) она не генерируется заново, а берется из кэша, что значительно ускоряет работу Drupal.
Если кэширование страниц включено в режиме "агрессивный", при генерации страницы пропускается загрузка и выгрузка включенных модулей, поэтому часть модулей могут работать некорректно или не работать совсем.
(рис 8.2) Настройки производительности для DrupalНастроим минимальное время жизни кэша страниц для анонимных пользователей. Данный параметр определяет, через какое время после кэширования страницы производится проверка на то, обновлено ли содержимое данной страницы или нет. Если обновлено, то кэш данной страницы очищается. Т.е. если администратор сайта изменил содержимое страницы, он его увидит сразу, а анонимные пользователи — только по прошествии минимального времени жизни кэша.
Включим компрессию страниц для сохранения сжатого кэша страниц и для передачи страницы браузеру пользователя в сжатом виде, если он поддерживает компрессию gzip. Компрессия производится с помощью библиотеки zLib, установленной как расширение в PHP.
Включим кэширование блоков. Принцип работы кэширования блоков аналогичен принципу кэширования страниц. Для супер-пользователя (первого зарегистрированного пользователя при установке DrupaL, его id равен 1) блоки никогда не кэшируются.
Включим оптимизацию CSS- и JavaScript-файлов. Это уменьшит их размер и количество обращений к серверу при загрузке страниц в браузер. Все CSS- и JavaScript-файлы собираются в один (свой файл для CSS (обычно их бывает два: один для отображения на экране, другой — для отображения при печати) и свой — для JavaScript). Таким образом мы уменьшим количество обращений к серверу при загрузке страницы.
Authcache сохраняет сжатый кэш страниц отдельно для каждого пользователя или роли. Кэш сохраняется в базе данных или в стороннем средстве кэширования (memcahed,
Для установки модуля:
/sites/all/modules ;index.php )/sites/default ) и добавим
следующий код (без примечаний за // ...) в начало файла после
тега <?php:$conf['cache_inc'] = './sites/all/modules/authcache/api/authcache.inc'; $conf['authcache'] = array( 'default' => array( // технология кэширования - apc, memcache, db, file, // eacc or xcache 'engine' => 'db', // если используем memcached (host:port, например, // 'localhost:11211') 'server' => array(), // если используем процесс memcached, shared или single 'shared' => TRUE, // кэш ключа префикса (для нескольких сайтов) 'prefix' => '', // если используем кэширование на файлах — указываем // путь их сохранения 'path' => 'files/filecache', // статический массив кэша (расширенный) 'static' => FALSE, ), );
В данном коде устанавливаются настройки модуля Authcache. Указываем, что будем хранить кэш страниц в базе данных ('engine' => 'db'), поэтому все остальные установки не имеют значения, и мы оставляем их без изменений. Более подробно о параметрах данного кода можно прочитать на странице http://drupaL.org/project/cacherouter (на английском языке).
Включим модуль Authcache на странице "Управление — Модули" ( http://www.exampLe.ru/admin/buiLd/moduLes ). После чего настроим его работу на странице "Управление — Производительность — Authcache" ( http://www.exampLe.ru/admin/settings/performance/authcache ):
Invalidate all user sessions ) при первом запуске;Save clear cached pages ) для сохранения изменений.
(рис 8.3) Настройка модуля AuthcacheИзменим настройки элементов на страницах сайта так, чтобы страница для разных пользователей, принадлежащих одной роли, выглядела одинаково (т. е., например, запретим пользователям управлять видимостью блоков на сайте, если такие блоки существуют).
В шаблонах тем оформления используем переменные:
$user_name - для отображения имени аутентифицированного пользователя;$user_link - для отображения ссылок, связанных с профилем пользователя;$is_page_authcache — если установлен в TRUE, то все хуки данного шаблона темы оформления будут сохранены в кэш.Можно также ознакомиться с примером /sites/all/modules/authcache/modules/authcache_example, который показывает, как настроить блоки с пользовательским содержанием (с контентом пользователя).
Authcache и кэширует страницы
лучше встроенного кэширования Drupal. Скачаем модуль по адресу http://drupal.org/project/authcache . После скачивания распакуем модуль в
папку /sites/all/modules. Включим модуль Cache Router на странице "Управление — Модули"
(http://www.example.ru/admin/build/modules ).
Откроем файл settings.php (в папке /sites/default ) и добавим следующий код в начало файла перед тегом <?php:$conf['cache_inc'] = './sites/all/modules/cacherouter/cacherouter.inc'; $conf['cacherouter'] = array( 'default' => array( 'engine' => 'db', 'server' => array(), 'shared' => TRUE, 'prefix' => '', 'path' => 'sites/default/files/filecache', 'static' => FALSE, 'fast_cache' => TRUE, ), );
После осуществления действий, приведенных выше, страницы создаваемого сайта будут отдаваться сервером браузеру пользователя в сжатом виде, а вот CSS и JavaScript — нет. Исправим это:
/sites/all/modules ;"GZip CSS" и "GZip JavaScript" ;.htaccess, расположенный в корневой директории сайта (на основании данных из README.txt, входящего в состав модуля CSS Gzip), пропишем меду тегами <IfModule mod_rewrite.c> и </IfModule> следующий код:### START CSS GZIP ###
# Requires mod_mime to be enabled.
<IfModule mod_mime.c="">
# Send any files ending in .gz with x-gzip encoding
# in the header.
AddEncoding x-gzip .gz
</IfModule>
# Gzip compressed css files are of the type 'text/css'.
<FilesMatch "\.css\.gz$">
ForceType text/css
</FilesMatch>
<IfModule mod_rewrite.c="">
RewriteEngine on
# Serve gzip compressed css files
RewriteCond %{HTTP:Accept-encoding} gzip
RewriteCond %{REQUEST_FILENAME}\.gz -s
RewriteRule ^(.*)\.css $1\.css\.gz [L,QSA,T=text/css]
</IfModule>
### End CSS GZIP ###
Если в шаблонах темы оформления необходимо использовать дополнительные CSS и JavaScript, то желательно подключать их с помощью следующих команд:
drupal_add_css(‘путь к CSS относительно корневой директории сайта’);drupal_add_js(‘путь к JavaScript относительно корневой директории сайта’);для того, чтобы они оптимизировались (включались в один исходный CSS или JavaScript-файл) и сжимались совместно со всеми остальными CSS- или JavaScript-файлами, используемыми на сайте.
От оптимизации Drupal с помощью модулей, перейдем к более сложной оптимизации — оптимизации конфигурации и обслуживания Drupal.
/sites/default в файле settings.php изменим строкуini_set('session.gc_maxlifetime', 200000);
на
ini_set('session.gc_maxlifetime', 86400); // 24 часа (в секундах)
Также в этом файле можно сократить время жизни кэшированных страниц сеансов до 24 часов, изменив строку
ini_set('session.cache_expire', 200000);
на
ini_set('session.cache_expire', 1440); // 24 часа (в минутах)
Напоследок в этом же файле изменим время хранения cookie в браузере пользователя, сократив его до 24 часов:
ini_set('session.cookie_lifetime', 86400); // 24 часа (в секундах)
Если установить время хранения cookie в браузере пользователя равным 0, то cookie будет удаляться сразу после закрытия Интернет-браузера пользователем.
Так как сервер сайта может работать под управлением разных операционных систем:
то в каждом случае настройки оптимизации сервера будут отличаться (т.
е. установка eAccelerator в Windows и Lunux сильно различается). Ниже
приведены только основные рекомендации по оптимизации сервера. Подробно из рекомендаций рассмотрена лишь установка PHP-
sudo apt-get install php5-dev sudo apt-get install make
sudo cd /tmp/ sudo wget http://bart.eaccelerator.net/source/0.9.5.3/eaccelerator- 0.9.5.3.tar.bz2 sudo tar xvjf eaccelerator-0.9.5.3.tar.bz2 sudo cd eaccelerator-0.9.5.3 sudo phpize sudo ./configure —enable-eaccelerator=shared sudo make sudo make install
; eAccelerator configuration ; Note that eAccelerator may also be installed as a PHP extension or as a zend_extension ; If you are using a thread safe build of PHP you must use ; zend_extension_ts instead of zend_extension ;extension = "/usr/lib/php5/20060613+lfs/eaccelerator.so" zend_extension = "/usr/lib/php5/20060613+lfs/eaccelerator.so" eaccelerator.shm_size = "16" eaccelerator.cache_dir = "/var/cache/eaccelerator" eaccelerator.enable = "1" eaccelerator.optimizer = "1" eaccelerator.check_mtime = "1" eaccelerator.debug = "0" eaccelerator.filter = "" eaccelerator.shm_max = "0" eaccelerator.shm_ttl = "0" eaccelerator.shm_prune_period = "0" eaccelerator.shm_only = "0" eaccelerator.compress = "1" eaccelerator.compress_level = "9" eaccelerator.allowed_admin_path = "/var/www/eaccelerator"
; eAccelerator configuration ; Note that eAccelerator may also be installed as a PHP extension or as a zend_extension ; If you are using a thread safe build of PHP you must use ; zend_extension_ts instead of zend_extension ;extension = "/usr/lib/php5/20060613+lfs/eaccelerator.so" zend_extension = "/usr/lib/php5/20060613+lfs/eaccelerator.so" eaccelerator.shm_size = "16" eaccelerator.cache_dir = "/var/cache/eaccelerator" eaccelerator.enable = "1" eaccelerator.optimizer = "1" eaccelerator.check_mtime = "1" eaccelerator.debug = "0" eaccelerator.filter = "" eaccelerator.shm_max = "0" eaccelerator.shm_ttl = "0" eaccelerator.shm_prune_period = "0" eaccelerator.shm_only = "0" eaccelerator.compress = "1" eaccelerator.compress_level = "9" eaccelerator.allowed_admin_path = "/var/www/eaccelerator" ; ionCube Loader configuration zend_extension=/usr/local/lib/ioncube/ioncube_loader_lin_5.2.so ; Zend Optimizer configuration zend_extension=/usr/local/lib/Zend/ZendOptimizer.so zend_optimizer.optimization_level=15
sudo mkdir -p /var/cache/eaccelerator sudo chmod 0777 /var/cache/eaccelerator
sudo /etc/init.d/apache2 restart
mod_expires , который позволяет Drupal посылать HTTP-заголовки Expires , кэшируя все статические файлы
(изображения, CSS, JavaScript и т. п.) в Интернет-браузере пользователя на определенный срок или до момента появления новых версий файлов. Настройки взаимодействия
Drupal и модуля mod_expires веб-сервера Apache находятся в файле .htaccess в корневой директории сайта:# Включить mod_expires. <IfModule mod_expires.c=""> # Разрешить истечение срока. ExpiresActive On # Кэшировать все файлы на две недели после доступа (A). ExpiresDefault A1209600 # Не кэшировать динамически генерируемые страницы. ExpiresByType text/html A1 </IfModule>
.htaccess файлов веб-сервером их содержание можно перенести в главный файл конфигурации Apache — .htaccess в
пределах корневого каталога веб-сервера, установив AllowOverride в None:<Directory/> AllowOverride … </Directory>
.htaccess в httpd.conf не пропустить не один файл .htaccess.my.cnf в папке /etc/mysql (при использовании Ubuntu 8.04). Изменим значение# query_cache_limit = 1M # query_cache_size = 16M
на
query_cache_limit = 1M query_cache_size = 64M
После чего перезапустим MySQL командой
/etc/init.d/mysql restart
Слишком маленький размер кэша — малоэффективен, а слишком больой размер кэша приводит к тому, что поиск нужной информации в кэше тратит много времени. Поэтому рекомендуем поэкспериментировать с размером кэша на каждом конкретном сервере и подобрать его оптимальный размер.
settings.php (в папке /sites/default ) — либо разместим сайт на кластере
из серверов (например, воспользовавшись услугами сервиса Amazon C2).Для просмотра информации о сервере из Drupal существует удобный модуль — System information (http://drupal.org/project/systeminfo ). После его установки и активации информацию о вашем сервере можно посмотреть на странице http://www.example.ru/admin/reports/systeminfo.
Ускорение работы любой системы возможно, в основном, за счет кэширования некоторых (тут стоит подчеркнуть, что именно некоторых, а не всех подряд) часто используемых операций. Все кэширующие мероприятия, в том числе и для Wordpress, можно разбить на несколько основных частей:
Данную проблему можно проиллюстрировать при помощи следующего рисунка:
Так уж сложилось, что основное узкое место практически любой системы заключается в базе данных, поэтому ее стараются ускорить всеми возможными способами. Стоит отметить, что проблема многочисленных вызовов к
базе данных не решается просто уменьшением их количества (для предоставления той же самой информации), тут надо подходить более комплексно и настраивать многоуровневый кэш для запросов. Относительно
MySQL это сделать довольно просто: достаточно прописать в конфигурационном файле my. (или my.ini ) следующие параметры (в случае большого количества оперативной памяти 20 Мб
может быть увеличено до любого
приемлемого количества, но не стоит здесь увлекаться: скорость поиска данных в кэше напрямую зависит от размера самого кэша):
(рис 8.4) Кэширующие звенья для Wordpress, источник: www.arnebrachhoLd.dequery-cache-type=1 query-cache-size=20M
Для оптимизации таблиц (что позволит уменьшить время запросов на 20—50%) можно воспользоваться дополнением Optimize DB
(http://yoast.com/wordpress/optimize-db/ ), которое позволит существенно уменьшить размер таблиц MySQL и улучшить их структуру.
Для кэширования запросов к базе данных также существует специальное дополнение, DB Cache
Каждый раз, когда исполняется
Следующим шагом для борьбы с большим временем подготовки страницы на сервере будет полное кэширование создаваемой страницы в один файл или одну запись в оперативной памяти. Для включения внутреннего кэширования на
уровне самого Wordpress достаточно раскомментировать (или добавить) в файл wp-config.php следующие строки (предварительно проверив, что директория wp-content/cache доступна для записи, иначе
ничего не получится):
define('ENABLE_CACHE', true );
define('CACHE_EXPIRATION_TIME', 900);
Более серьезных результатов кэширования можно добиться при помощи дополнения WP-Super-Cache (http://ocaoimh.ie/wp-supercache/ , базирующегося на WP-Cache, http://mnm.uib.es/gallir/wpcache-2/) или Hyper Cache (http://www.satollo.com/english/wordpress/hyper-cache), которое вообще не будет осуществлять никаких запросов к базе данных для отображения внешних веб-страниц. Однако при этом станет невозможно учитывать статистику посещений через встроенные в Wordpress методы (только через внешние счетчики или по логам сервера). Для Wordpress, установленного на IIS, также лучше всего будет использовать именно WP-Super-Cache вместо IIS Output Caching. Это подробно рассматривается в соответствующей заметке; ниже приведено число запросов в секунду при том или ином методе серверного кэширования.
Но давайте посмотрим, что можно сделать с клиентской составляющей (дизайном и скриптами) обычного блога.
(рис 8.5) Рис. 8.5. Производительность кэширования Wordpress для IIS, источник: bLogs.iis.net
Основной минус богатства тем для Wordpress — то, что они в основном разрабатываются любителями. Как следствие, такие темы могут состоять из большого количества картинок и файлов стилей, которые в совокупности загружаются очень медленно. Однако ситуация поправимая. Для ускорения загрузки сайта в самом браузере (а это, по мнению экспертов Yahoo!, занимает 95% времени общей загрузки страницы) можно воспользоваться несколькими решениями:
.htaccess-файл (что обеспечивает более широкую совместимость и снимает нагрузку с В общем, даже самый обычный блог может быть ускорен в несколько (десятков) раз за считанные минуты. Скорее всего, в ближайшем будущем уже появятся отдельные сборки Wordpress, настроенные на максимальную производительность при задействовании любой темы и произвольной посещаемости блога. Сейчас же можно просто воспользоваться вышеприве- денными советами и порадоваться за значительное увеличение числа посещений и постоянных читателей.
Скорее всего, многие уже слышали о медленной работе JoomLa! (http://www.joomLa.org/), одной из самых популярных бесплатных
Естественно, что у такой простоты есть и обратная сторона: большинство расширений создаются без учета требований высокой производительности и какой-либо оглядки на мощности конечных серверов, очень часто даже не выделенных или виртуальных, а расположенных на общем хостинге. Разработка бесплатных решений оборачивается 100-500% замедлением в скорости загрузки сайта. Давайте разбираться, как с этим можно бороться.
Для начала посмотрим, что можно сделать с серверной производительностью. Мы исследовали стандартную сборку JoomLa!, но даже в такой комплектации на отдельном сервере время создания страницы занимало 0,312 с (замер времени ответа производился с помощью curl, интерфейс к которому выложен на webo.in,http://webo.in/my/action/timings/). Это не очень много, но в условиях виртуального хостинга может возрасти многократно, даже на изначально хорошо оптимизированных окружениях.
Встроенное кэширование в JoomLa! 1.5 работает достаточно хорошо и позволяет сэкономить существенное время при создании страницы. Однако нужно понимать, что оно может быть применимо далеко не для всякой системы. В случае статичных новостных сайтов и небольших интернет-магазинов кэширование может помочь, но для социальных сетей с активным добавлением новых материалов и их комментированием оно точно не подойдет.
Кэширование на стороне сервера
Включение встроенного кэширования в Joomla! 1.5 сократило время ответа тестируемого сервера примерно на 30% (на 0,107 с). Прекрасно понятно, что в большинстве случаев оно будет практически бесполезно: если необходимо сократить время создания страниц на порядок, то нужны более кардинальные методы.
В качестве одного из кэширующих решений может использоваться и Web Optimizer: встроенное кэширование HTML-документов позволяет отдавать их сразу в том виде, в котором они получаются системой после всех запросов к базе. При этом, естественно, практически все эти запросы не осуществляются. Данное кэширование ("монолитное") подойдет только в тех случаях, когда внешние страницы у Joomla! меняются относительно редко.
Если просто включить Web Optimizer ( http://code.google.com/p/weboptimizator/ ) в процесс создания страниц, то время обработки документа возрастет незначительно (после создания всех кэширующих файлов на 0,006 с или 3% на тестовом сервере). Дополнительно включив HTML-кэши- рование в Web Optimizer, можно сократить время отдачи документа до 0,08 с (почти в 4 раза по сравнению с исходным временем создания страницы). Сразу стоит отметить, что установка вроде бы аналогичного по функциональ- ности дополнения Content Static ( http://extensions.joomla.org/extensionssite-management/cache/5104/details/ ) визуально на производительности никак не отразилась.
Очевидно, что более грамотным будет кэшировать отдельные модули на странице, оставляя нужные места (или, как их любят называть в шаблонных движках, — заглушки) динамическими. Однако данное решение требует существенного
вмешательства в алгоритм работы самой
Заканчивая речь про кэширование создаваемых страниц на стороне сервера, стоит упомянуть, что дополнение JoomLa Performance
Кэширование запросов к базе данных
К серверному кэшированию можно подойти и с другой стороны: ограничить число запросов к базе данных, — обычно именно эта часть вызывает наиболее серьезную "утечку" производительности. Для JoomLa! существует расширение, позволяющее закэшировать все (или почти все) запросы к базе. Тут стоит понимать, что база данных сама по себе может работать достаточно быстро, и подобное решение будет эффективно только в том случае, если восстановление закэшированного значения выборки на порядок (или хотя бы в разы) быстрее, чем осуществление самой выборки (например, 1 мс против 10 мс). В противном случае прироста производительности не произойдет.
Для оценки эффективности решений для клиентской оптимизации использовалось хорошо зарекомендовавшее себя (и относительно беспристрастное) дополнение к Firefox — YSLow.
"Чистая" система
"Чистая" установка Joomla! 1.5 набрала 65 баллов из 100. Вполне приемлемо. Стоит понимать, что если на систему дополнительно поставить десяток модулей и компонентов, то оценка резко ухудшится до 30-40.
Следующий этап: архивирование
В Joomla! есть встроенный gzip. Однако, во-первых, он работает через PHP, во-вторых, только для HTML-файлов. Грустно, что и видно по оценке: она поднялась только до 67.
CssJsCompress
Довольно известное дополнение (http://extensions.joomla.org/extensions/site-management/cache/7350/details), позволяющее объединять CSS- и JS-файлы. Однако оно не добавляет к ним всех кэширующих заголовков и сжатия, что и отразилось на результате: всего 72 балла по YSlow. В самой Joomla! gzip при этом был включен. Дополнение CSS/JS Cache ( http://extensions.joomla.org/extensions/site-management/cache/7801/details) не удалось заставить корректно работать."
Joomla Perfomance Booster
Однако данное дополнение возможно подключить вместе с приложением Web Optimizer (которое возьмет на себя всю логику преобразования клиентской части), что позволит существенно ускорить работу сайта на Joomla! практически любой сложности.
Smart Optimizer
Далее был протестирован Smart Optimizer (http://farhadi.ir/works/smartoptimizer, как отдельное PHP-приложение) — по характеру работы полностью аналогичный известному Minify (http://code.google.com/p/minify/, дополнение Minify4Joomla, http://extensions.joomla.org/extensions/site-management/cache/7183/details , "завести" не удалось). Установка у него достаточно сложная для непрофессионала, к тому же приходится править шаблоны вручную, нет возможности объединять файлы из разных директорий. Однако все остальное на высоте: оценка поднялась до 85. В самой Joomla! gzip при этом был включен."
Web Optimizer
На данный момент для Joomla! 1.5 не существует более мощного бесплатного решения для оптимизации производительности, чем Web Optimizer. PHP Speedy (http://code.google.com/p/phpspeedy/), к сожалению, доступен только для Joomla! 1.0.
Материал для данного раздела и консультирование по вопросам производительности Joostina
предоставил Николай Кирш — веб-разработчик,
основатель и технический лидер проекта Joostina
Joostina родилась и развивается с изначальной целью: быть максимально быстрой и
эффективно использовать ресурсы сервера, не уменьшая при этом удобств как для пользователя, так и для администратора сайта. В основе системы лежит
Оптимизацию
Все настройки указаны для Joostina версии 1.3.0.
Самый быстрый и безопасный способ настроить свой сайт на более высокую скорость и выжать из него максимум возможностей — основательно ознакомиться с настройками, располагающимися в "Глобальной конфигурации". Для доступа к настройкам необходимо авторизоваться с правами Супер-администратора в административной части, называемой так же панелью управления: http://www.exampLe.ru/administrator. Далее надо выбрать пункт меню "Сайт — Глобальная конфигурация", или прямо на главной страницы панели управления, через кнопку быстрого доступа "Глобальная конфигурация".
Отключить генерацию RSS (syndicate)
Joostina, как и большинство современных
<link rel="alternate" type="application/rss+xml" title="Joostina v 1.3.0 b" href="http://www.example.ru/index2.php?option=com_rssfeed=0amp;no_html=1" />
Но формирование ссылки на ленту занимает определенное время, оно нужно на запрос в базу данных на получение параметров отображения. Если же тег ленты необходим, а избавиться от лишнего запроса тоже хочется, — можно прописать ссылку напрямую в шаблоне сайта.
Использовать шаблон
Для каждого пункта меню в панели управления можно выбрать уникальное отображение и состав модулей. Но также для каждого из пунктов меню можно назначить уникальный шаблон. Если такая возможность на сайте не используется, то ее следует отключить. Для этого и создана данная настройка. Параметр позволяет выбрать единый шаблон для всего сайта, что исключит один запрос и его обработку для выбора конкретного шаблона. Аналогичная настройка существует для панели управления.
Отключить мамботы группы system
Мамботы — это чаще всего небольшие PHP-сценарии, срабатываю щие на определенном этапе работы системы. Мамботы группы system сра- батывают в момент инициализации системы. Обычно в группу входят расширенные обработчики SEF и библиотеки подключения Javascript. Все мамботы этой группы можно посмотреть в панели управления "Меню - Мамботы -Мамботы сайта". Если справа в списке выбора типа нет группы system, то настройку рекомендуется отключить, это сделает ненужным один запрос в базу и инициализацию механизма.
Отключить мамботы группы content,
Отключить мамботы группы mainbody
Действие данного пункта аналогично группе system, но поступать тут надо внимательнее. Группа content — основная, за счет нее выводятся изображения, вставленные в текст через тег {mosimage},
разбивка на страницы внутри текста и т. д. Безопаснее всего поочередно снимать мамботы с публикации и смотреть, что изменилось на сайте. Если все мамботы не опубликованы, а сайт отображается верно, — можно
отключить всю группу.
Использовать неопубликованные мамботы
Мамботы группы content часто работают, заменяя определенные те-
ги в тексте, например, {mosimage}. Но если мамбот не опубликован, то система его все равно использует — чтобы убрать из текста этот самый тег {mosimage}. Если на сайте такие мамботы не используются, то лучше активировать данную настройку, исключив неиспользуемые обращения к
базе данных и подключение лишних файлов.
Авторизация на сайте
Отключив данный пункт, вы запретите инициализацию пользователей на сайте. Параметр запретит заведение пользовательских сессий в базе данных и позволит более полно кэшироваться страницам. Если на сайте не предусмотрена работа пользователей, то и авторизацию лучше отключить.
Время существования сессии на фронте
При авторизации для пользователя заводится специальная сессия, данные о ней записываются в базу данных и имеют определенный срок жизни: пока сессия жива — пользователь считается авторизованным. Если указать в настройке большое время жизни сессии, то в базе данных сессий будет довольно много значений, что повлечет дополнительную на грузку для поиска сессий конкретного пользователя
Отключить сессии на фронте
При посещении сайта авторизованным пользователем или даже гостем для него запускается механизм инициализации сессий, что влечет за собой запись данных в базу, создание cookie у пользователя и постоянную проверку авторизации. Если авторизация на сайте не важна, то параметр рекомендуется отключить. Но учтите, что модуль отображающих посетителей будет выдавать не точную информацию, так как он основывается на данных записанных в таблице сессий. Параметр рекомендуется использовать совместно с пунктом "Авторизация на сайте".
Отключить контроль доступа к содержимому
Хотя в Joostina имеется не очень много возможностей для полноценного создания и управления правами пользователей, доступ к содержимому всегда ведется с учетом прав текущего пользователя. Это добавляет в SQL-запрос дополнительное условие. На сайтах, где доступ не разграничен на зарегистрированных и гостей, параметр лучше активировать.
Считать число прочтений содержимого
При прочтении каждого содержимого увеличивается значение поля
счетчика в таблице содержимого. Постоянные изменения даже одного поля таблицы содержимого сводят на нет встроенный в mysql механизм кэширования, да и дополнительный запрос в базу тоже лучше исключить.
Настройку рекомендуется выключить, а ведение статистики доверить специализированным сервисам, типа li.ru или
Отключить проверки публикации по датам
Для каждого материала при редактировании можно указать определенные периоды начала и окончания публикации. Проверка по датам добавляет в каждый SQL-запрос условие соответствия текущей дате, а настройка данное условие отключает. Чем меньше условий в запросе, тем легче его будет отработать базе данных и тем быстрее пользователь сайта увидит ожидаемые страницы.
GZIP-сжатие страниц
Позволяет передавать пользователю более компактные страницы. Содержимое пакуется через gzip-алгоритм на сервере, а распаковывается автоматически в браузере пользователя. Получается экономия на трафике, но немного больший расход ресурсов сервера; более подробно расчет оптимальности применения gzip приведен во второй главе книги "Разгони свой сайт".
Блокировка компонентов
Позволяет отключить прямой доступ к компонентам, набрав специальный адрес в браузере. Если на сайте имеется компонент, но он не используется, то настройку лучше активировать.
Рейтинг/Голосование
В базовой поставке системы имеется мамбот группы content, позволяющий выставлять рейтинг для каждого материала. Если такая возможность не требуется, рейтинг лучше отключить, — это исключит лишние проверки и инициализации.
Ежедневная оптимизация таблиц базы данных
Добавляет в работу сайта ежедневную оптимизацию всех таблиц базы данных через выполнение OPTIMIZE TABLE для каждой. Данная процедура уменьшает фрагментацию и производит общую оптимизацию таблиц встроенными средствами mysql.
Сжатие CSS- и JS-файлов
Позволяет выдавать вместо обычных JS- и CSS-файлов их упакованные аналоги. Экономит трафик и позволяет указать более длительное время кэширования. Работает только для встроенных файлов.
Значение тега revisit:
Позволяет указать параметр тега:
<meta name="revisit" content="10 days" />
который указывает, как часто сайт должен посещать
Включить кэширование
В Joostina встроен механизм, позволяющий кэшировать результаты выполнения ресурсоемких функций или инициализаций объектов. Использование кэширования позволяет уменьшить число запросов в базу, уменьшить число подключаемых файлов и увеличить общую скорость работы системы. Кэширование лучше активировать после полного построения и отладки сайта.
Тип кэширующей системы
Кэшировать можно как в файлы, так и в специальные
Оптимизация кэширования
При активации параметра из кэшируемых объектов будут удалены все неиспользуемые символы, например, переводы строк или множественные пробелы. Это уменьшает общий объем файла кэша и немного уменьшает трафик, передаваемый пользователю.
Автоматическая очистка каталога кэша
При кэшировании в файлы каталог кэша может содержать множество просроченных объектов. Система следит, чтобы таких случаев не было, но для большей уверенности рекомендуется активировать и этот параметр. На оптимизацию работы сайта число файлов в каталоге кэша имеет прямое влияние: чем больше файлов в каталоге одного уровня — тем дольше файловая система будет их отдавать.
Кэширование меню панели управления
При работе в панели управления в верхней части отображается меню, которое содержит пункты для доступа к основным операциям. Меню частично формируется из базы данных. Например, список установленных компонентов или список разделов и категорий. Такие данные изменяются не очень часто, и лучше произвести кэширование этого участка. Активация параметра также сделает вывод меню через внешний JavaScript- файл, код которого исключится из тела страниц и будет кэшироваться еще и на стороне пользователя — в браузере.
Каталог кэша (/dev/shm)
По умолчанию файлы кэша складываются в каталог /cache в корне
сайта. В зависимости от настроек сервера можно попытаться перенести
этот каталог в более быстрое место, например, на диск с другой файловой
системой или /dev/shm. Не забудьте убедиться, что PHP-интерпретатор
имеет полный доступ к указанному каталогу
Время жизни кэша
Позволяет указать период времени, на который должно срабатывать встроенное кэширование. Если сайт имеет не очень частое изменение структуры и содержимого или слабую активность пользователей, то время лучше указать больше. Выбранный период используется по умолчанию для всего кэша. Но в настройках модулей можно дополнительно выбрать, на какой период их кэшировать.
Включить сбор статистики
Параметр отвечает за исключение из работы системы сбора информации о браузере и других данных, которые лучше собирать через специальные сервисы, озвученные выше. Рекомендуется отключить.
Вести статистику просмотра содержимого по дате
Аналогично предыдущему параметру — лучше отключить и вести все учеты на серверах специальных сервисов.
Статистика поисковых запросов
Joostina сохраняет информацию о каждом слове, которые пользователи ищут на сайте. Возможность позволяет частично проанализировать пользовательскую аудиторию и узнать их интересы. Если такой функционал не требуется — лучше отключить.
Один из основных принципов оптимизации — использование только необходимого функционала. Joostina по своей сути является универсальной системой, и это влечет за собой некоторую ограниченность и сложность. Базовый дистрибутив системы имеет набор встроенных расширений, часть которых может не использоваться на сайте. Всего в Joostina можно отключить расширения всех 3-х типов:
Отключение компонентов лишь косвенно влияет на оптимизацию
сайта. Но блокировка доступа — это отличный способ уменьшить число
страниц для индексации или попыток спама. Чем меньше страниц, тем быстрее их проиндексирует
Отключить неиспользуемые компоненты можно в панели управления на странице управления компонентами: "Меню - Компоненты - Управление компонентами". Для работы данного механизма необходимо, чтобы в глобальной конфигурации была активирована настройка "Блокировка компонентов".
Отключение модулей позволяет уменьшить чисто ненужных запросов в базу, подключение неиспользуемых файлов и общий расход памяти, выделяемой на генерацию страницы. Выяснить, какие модули действительно необходимы, можно по следующей схеме.
Заходим в меню управления модулями: "Меню - Модули - Модули сайта".
Выбираем все модули и нажимаем кнопку "Скрыть", находящуюся в верхней части страницы — на тулбаре.
В новой вкладке или новом окне открываем главную страницу сайта и поочередно публикуем нужные модули. Начать лучше с модуля главного меню. Перед публикацией каждого модуля подумайте, нужен ли он, и попутно запоминайте, сколько новых запросов прибавилось. Если какой-то из модулей создает слишком большую нагрузку — придется поискать его более простые аналоги или найти способы оптимизации кода.
Отключение мамботов позволяет существенно сократить непосредственное время генерации страницы. Мамботы разделены на группы, про это уже сообщалось ранее, и каждая группа
отвечает за отдельные участки. Наиболее часто используемые — мамботы группы content, они позволяют обрабатывать содержимое, выдаваемое компонентом. Чаще всего такие
мам-боты отвечают за замену в тексте специально оформленных тегов на необходимый функционал или оформление. Например, мамбот bot_mosimage отвечает за замену тега {mosimage}
на необходимую картинку. Но для такой работы производится обработка текста регулярными выражениями, что не очень хорошо сказывается на производительности. Отключить неиспользуемые мамботы можно по той же
схеме, что и модули, только на другой странице панели управления: Меню — Мамботы — Мамботы сайта.
Если все мамботы группы отключены, то лучше отключить и саму группу — это делается в глобальной конфигурации для каждой группы отдельно.
Уже много людей писали руководства, помогающие вашему веб-приложению работать быстрее. В этом разделе будут освещены самые простые, но наиболее эффективные методы, которые дадут вам возможность
существенно ускорить ваше приложение без потери какого-либо функционала из
Часто бывает так, что одно веб-приложение подгружает сразу несколько JavaScript-файлов и CSS-стилей. Это существенно замедляет загрузку страницы, так как веб-браузер каждый раз заново запрашивает новый файл.
Решение заключается в том, чтобы уменьшить количество внешних ресурсов на вашей странице, объединив их все в один файл. Поможет нам в этом плагин AssetPackager (http://synthesis.sbecker.net/pages/asset packager). Ставим
script/plugin install git://github.com/sbecker/asset_packager.git
Пример config/asset_packages.yml:
javascripts: - base: - prototype - effects - controls - dragdrop - application - secondary: - foo - bar stylesheets: - base: - screen - header - secondary: - foo - bar
И запускаем rake-задачу:
rake asset:packager:build_all
Дальше для JavaScript пишем
<%= javascript_include_merged :base %>
или
<%= javascript_include_merged 'prototype', 'effects', 'controls', 'dragdrop', 'application' % >
Для стилей пишем:
<%= stylesheet_link_merged :base %>
или
<%= stylesheet_link_merged 'screen', 'header' %>
В итоге получаем для режима разработки наш старый код, например:
<script type="text/javascript" src="/javascripts/prototype.js"></script> <script type="text/javascript" src="/javascripts/effects.js"></script> <script type="text/javascript" src="/javascripts/controls.js"></script> <script type="text/javascript" src="/javascripts/dragdrop.js"></script> <script type="text/javascript" src="/javascripts/application.js"></script> <link href="/stylesheets/screen.css" type="text/css" /> <link href="/stylesheets/header.css" type="text/css" />
А в режиме рабочего сайта будет:
<script type="text/javascript" src="/javascripts/base_packaged.js?123456789"></script> <link href="/stylesheets/base_packaged.css?123456789" type="text/css" />
Теперь, чтобы сделать нагрузку еще меньше, переносим все свои статические файлы на другой хост. В
config.action_controller.asset_host = "http://assets.example.ru"
Теперь все image_tag, javascript_include_tag и т. д. будут указывать на этот хост.
Материал для данного раздела получен при общении с Олегом Смирновым (
К сожалению, большая часть всех плагинов имеет код низкого качества, поэтому при установке нескольких таких плагинов страница начинает существенно подтормаживать. О том, как поправить код чужого плагина, чтобы избежать лишнего расхода памяти при сложных манипуляциях с DOM-деревом, как сделать анимацию плавной даже при анимировании нескольких элементов, а AJAX — быстрым, а также о том, как избежать некоторых скрытых багов, и будет этот раздел.
Стоит начать с функции $, она принимает два параметра: первый — селектор, второй — контекст. Хотя контекст обычно опускают, впоследствии будет показано, как им грамотно пользоваться.
Простой селект
Самый простой вариант — это выбор по id, имени тега и имени класса.
$("#id")
$("tag")
$(".class")
Не случайно они расположены именно в такой последовательности: они идут по сложности алгоритма выборки. В первом случае вызов функции эквивалентен вызову
document.getElementById("id");
Поскольку предполагается, что id уникальный, поиск проходит очень быстро, и если на странице есть два элемента с таким id, то найден будет только первый. Хотя в IE и тут сделали ошибку и до 7-й версии включительно в случае отсутствия элемента с таким id он вернет элемент, у которого совпадает атрибут name.
Во втором случае тоже все относительно просто:
document.getElementsByTagName("tag");
Получили все ноды с таким именем из документа, и все готово. И на
удивление никаких ошибок, если не учитывать, что при запросе getElementsByTagName("*") IE вернет и комментарии тоже.
В третьем случае, если есть возможность, работу перехватывает
document.getElementsByClassName("class");
(Таблицу поддержки этой функции в браузерах можно посмотреть на quirksmode.org)
Эту функцию поддерживают еще не все браузеры. Для остальных применяется совсем другой алгоритм: нужно получить абсолютно все ноды, потом обойти их циклом, проверяя имена классов, и если совпали, то добавить в массив результата.
var nodes = document.getElementsByTagName("*"), result = [];
for (var i=0; i<nodes.length ; i=""
if="" ( " " + (nodes=""[i=""].className=""
nodes=""[i=""].getAttribute=""
.indexOf=""("class") >-1)
result.push(nodes[i]);
}
Какой метод применять, определяется в самом начале при подключении библиотеки.
Селект через querySelectorAll
приходится обычно использовать намного более сложные конструкции. И для них в современных браузерах FireFox 3.0, Safari 3.2, Opera 9.5,
а также в IE8, появились функции querySelector и querySelectorAll.
Они, соответственно, предназначены для поиска одной или нескольких
нод по CSS3-селекторам. Если браузер клиента поддерживает эту функцию, то все, о чем написано в прошлом пункте, — отпадает, и поиск происходит через querySelectorAll.
$("#id .class tag")
В лучшем случае селектор будет обработан именно querySelectorAll, потому что он написан по правилам CSS3. Но такое
возможно не со всеми селекторами: visible.
$("#id .class tag:visible")
Такой селектор выдаст ошибку в функции querySelectorAll, и селектор будет перенаправлен в поисковый движок Sizzle, где строка будет разбита на простые селекторы и превратится, по сути, в несколько разных поисков, в котором каждым следующим контекстом является предыдущий селектор.
$(document).find("#id").find(".class").find("tag").filter(":visible")
Скорость этого метода поиска напрямую зависит от величины DOM дерева: чем оно больше, тем медленнее, — но ее можно значительно увеличить, написав селектор раздельно.
$("#id .class tag").filter(":visible")
При этом querySelectorAll выберет все ноды, а Sizzle разберется с ":visible".
По поводу псевдо-селекторов возникает также очень интересный вопрос: CSS3 поддерживает несколько видов псевдо-классов, такие как :nth-of-type/:nth-child/:parent/:not/:checked, querySelectorAll не поддерживает данный селектор, но эта реализация иногда отличается.
Для примера возьмем псевдо-класс : nth-of-type и выберем все четные дивы, а из них все нечетные.
document.querySelectorAll("div:nth-of-type(even):
nth-of-type(odd)")
// Safari/FireFox:0 IE/Opera:N/A
$("div:nth-of-type(even):nth-of-type(odd)");
// Safari/FireFox:0 IE/Opera:All
$("div:even:odd"); // All: вернут 1,5,9 дивы
Первых два примера работают одинаково и вернут либо 0, если отра-
ботала функция querySelectorAll (это касается первого примера), либо
все элементы, потому что их обработал Sizzle (это особенность реализа-
ции выражения ":"). Третий же вернет 1, 5, 9 и т. д. элементы, а значит,
селекторы отрабатывали в три прохода: сначала из всего DOM-дерева бы-
ли выбраны все дивы, потом из них были выбраны все нечетные, а потом
из оставшихся были выбраны все четные.
visible/:animated/:input/:header. Их
лучше выделять отдельно, поскольку они могут сильно замедлить выборку.
Так, например, было с селекторами :visible/:hidden в версии 1.2.6: для то-
го чтобы узнать, видимый это элемент или нет, надо было подняться до са-
мого верха по DOM-дереву, проверяя атрибуты display и visible каждого ро-
дителя (http://mabp.kiev.ua/2009/02/07/accelerates-selectors-in-jquery/).
$("div").filter(":visible")
Псевдо-классы, используемые для поиска элементов формы, такие, как :radio, тоже имеют некоторое преимущество, если не применяется querySelectorAll; в противном случае CSS3-селектор input[type=radio] работает быстрее.
Сложенный селект
Сложенный селект — это когда нам надо выбрать группу из двух или более разных селекторов, например, все дивы, у которых класс равен A, B и C.
Это можно сделать двумя способами:
$(".a,.b,.c")
выбрать все сразу
$(".a").add(".b").add(".c")
или по одному.
При этом, если задействована функция querySelectorAll, то первый способ быстрее второго в четыре раза, а если нет, то второй в два раза быстрее первого.
Если уже заговорили про классы, их можно искать как любые другие
атрибуты — например, если надо найти все классы, имена которых начинаются на "my", можно сделать так:
$("[class^=my]")
а не городить логику с использованием add, тем более что такой способ
поддерживается querySelectorAll (http://mabp.kiev.ua/2009/02/21/testingproductivity-jquery-selectors/).
Неправильный селект в контексте
$(‘#listItem’ + i, $(‘.myList’))
Рассмотрим подробнее: контекст — это то, где ищут селектор, значит, пример можно переписать в более наглядную, но менее читаемую форму:
$($(".myList")).find("#listItem")
При этом контекст от первого поиска будет являться document.
$($(".myList",document)).find("#listItem")
Еще раз перепишем согласно формуле
$($(document).find(".myList")).find("#listItem")
И наконец, раскроем скобки
$(document).find(".myList").find("#listItem")
Что же получается: мы выполняем дорогостоящую операцию поиска по имени класса (по всему DOM-дереву в худшем случае) для того, чтобы упростить и без того самую простую операцию поиска по id?
Правильный селект в контексте
Правильно делать "с точностью до наоборот". В контексте надо ука-
зывать id элемента.
$(".class",$("#id"))
Однако можно не передавать в контекст
$(".class","#id")
Это можно переписать как
$("#id").find(".class")
Можно еще больше ускорить работу, если искать вот таким способом:
$(document.getElementById("id")).find(".class")
Но это, скорее всего, будет уже дурным тоном. Хотя поэкспериментировать интересно: что, если вместо getElementById взять querySelectorAll?
$("div",document.querySelectorAll("#id"))
Это примерно то же самое, что и
$("div",[document.getElementById("id")])
Ни прироста производительности, ни красоты кода из этого не получить, поэтому советую в контекст передавать что-то простое вроде id или при использовании псевдо-селекторов, обрабатываемых Sizzle’ом, пере давать их в селектор а все остальное в контекст:
$(":visible","input[type=checkbox]")
Раз уже пошла речь о псевдо-селекторах, то
$(":checkbox")
быстрее чем
$("input[type=checkbox]")
без использования querySelectorAll и наоборот.
Cложный селект
Часто возникает задача найти всех потомков одного родителя. Если нам известно, что все потомки являются непосредственными, то есть детьми, на этом можно сэкономить. Конечно же, лучше всего было бы написать правильный селектор
$("#id > div")
Но если выборка уже есть, то будем использовать ее как контекст. Как мы уже выяснили, поиск в контексте происходит при помощи функции find:
$("#id").find("> div")
Но find — очень дорогая функция, она просматривает абсолютно всех потомков контекста, поэтому лучше применить функцию children, она просматривает только непосредственных потомков.
$("#id").children("div")
Есть еще ряд функций поиска и манипуляций, которых стоит избегать
без крайней необходимости, — это find, closest, wrap, wrapInner,
replaceWith, clone. Стоит заметить, что wrapAll сюда не входит
(http://mabp.kiev.ua/2009/03/29/jquery-profiling/).
Внутреннее кэширование
Кэш у
Ситуация следующая: вы работаете со списком, у вас есть один из элементов li. Для того, чтобы получить все элементы, включая текущий, надо выбрать всех братьев (все, у кого родитель —
это родитель текущего) этого элемента и добавить его самого:
$("#id").siblings().add("#id")
Так как он — прошлый элемент, с которым работали в цепочке вызовов, мы можем взять его из КЭШа:
$("#id").siblings().andSelf()
Конечно, в данном конкретном случаи быстрее было бы сделать
$("#id").parent().children()
Потому что
Второй пример использования кэша — это простой возврат к предыдущей выборке, вместо того чтобы размазывать код на три строчки:
var elt = $("#id");
elt.children().css({/**/})
elt.click();
Можно после работы с детьми вернуться обратно к родителю и работать с ним дальше:
$("#id").children().css({/**/}).end().click()
Кэширование селекторов
Поскольку кэш так слабо развит, селекторы нужно кэшировать вручную. Давайте рассмотрим, например, вот такой код:
for(var i=0;i<1000;i
$("ul").append=""("<li>"+i""/
Все работает и выглядит красиво, но и это можно оптимизировать. Если вынести выборку за пределы цикла, добавление новых элементов будет проходить быстрее:
var elts = $("ul");
for(var i=0;i<1000;i
elts.append=""("<li>"+i+"</li>")
Буферизация
Но этот код можно заставить работать еще быстрее! Каждый раз, делая append, мы заставляем обновиться DOM-дерево и заставляем браузер перерисовать страницу. Этого можно избежать, придерживая вставку в DOM-дерево.
var str = "";
for(var i=0;i<1000;i
str += ""<li>"+i+"</li>"
$("ul").html(str);
Дело в том, что функции для работы с DOM-деревом у html-ноды, на которые
повешены события через
html и text вызывают функции полной очистки и только потом вставки нового
содержимого:
jQuery(DOMElement).empty().append(text)
Функция empty выбирает все ноды и по очереди удаляет:
jQuery(DOMElement).children().remove()
А функция remove уже заботится, чтобы из элементов были удалены все дополнительные данные и события.
Джон Ресиг утверждал, что знает способ быстро удалить все это и что
улучшит эти методы, но что-то воз и ныне там. Поэтому будем ждать улуч-
шенных функций уже в
Создание "на лету"
Прошлый пример, на самом деле, был нужен
для того, чтобы подобраться поближе к
интересному факту. Часто приходится
создавать какие-то вспомогательные дивы,
и, естественно, нас интересует самый
эргономичный способ это сделать. Казалось
бы, в чем проблема: кинул кусок html-кода,
и
$("<div></div>")
или
$("<div/>")
Второй вариант в 5 раз быстрее первого. Но это, естественно, не все: если нам надо создать не пустой див, а содержащий текст, из прошлых заметок станет ясно, что функция text тяжелая, и выгоды от нее не будет, и стоит создавать див "как есть".
$("<div>text</div>")
И не создавать, а потом добавлять текст:
$("<div/>").text("text")
Но это не каcается создания атрибутов, для них используются намно- го более "легкие" функции attr/css/addClass (http://mabp.kiev.ua/2009/03/29/jquery-profiling/), вот тут-то и имеет смысл вместо
$("<div style='background:red;'/>")
писать
$("<div/>").css({background:'red'});
— это даст небольшой, но выигрыш.
Множественные события
$(window).bind("resize load",null,function(){
$("#id").css({width:document.clientWidth})
});
Только при этом не забываем, что поведение IE8 не соответствует стандартам, и при загрузке страницы сначала происходит событие resize, а только потом load.
То же самое корректно и в обратную сторону:
$(window).unbind("resize load");
Но это не работает в версии 1.2.6, точнее, работает только с именованными функциями, а с анонимными не годится, их надо удалять по одной.
Одно событие на много элементов
Если случается повесить события на длинный список:
var ul = $("<ul/>");
for(var i=0,j=1000;i<j;i++)
$("<li>"+i+"</li>").click(function(e){
alert(this.innerHTML);
}).appendTo(ul);
ul.appendTo("body");
то в результате мы будем иметь 1000 одинаковых обработчиков событий. Вряд ли это добавит скорости нашей странице, поэтому можно воспользоваться маленькой хитростью и повесить всего один обработчик на родительский элемент:
var str = "";
for(var i=0,j=1000;i<j;i
str += ""<li>"+i+"</li>";
$("<ul/>")
.append(str)
.click(function(e){
alert(e.target.innerHTML);
})
.appendTo("body");
Нередко можно наблюдать ситуацию, когда производительность сайта в клиентском браузере недооценивается. Особенно часто это происходит в тех случаях, когда сайт собран "из коробки" и не специализирован под выполнения тех или иных конкретных задач. Ситуация может быть дополнительно осложнена низкоскоростным каналом связи у целевой аудитории пользователей.
В общем, все вышеописанное было справедливо для сайта PERSPEK-TIVA
Для анализа скорости загрузки в качестве стандарта стоит использо-
вать как webo.in, так и ETag или Last-Modified, а также, что
более существенно, сразу виден потенциальный выигрыш при минимизации файлов.
С помощью визуальной оптимизации (http://webo.in/my/action/load/) удобно посмотреть, как изменится диаграмма загрузки сайта, если применить все оптимизационные меры. И поскольку расчет производится аналитически, он обеспечивает достаточно большую точность (не нужно замерять по два-три раза, чтобы избежать случайных сетевых задержек).
Разобрав основные проблемные места сайта с помощью указанных инструментов, намечаем путь действий — и вперед. Дополнительно мож- но оценить время ответа с сервера (http://webo.in/my/action/timings/) для динамических файлов и понять, нужно ли что-то придумывать для оптимизации серверной части.
Базовые действия по оптимизации чрезвычайно просты: нам нужно объединить все текстовые файлы и применить для них gzip-сжатие. А также включить кэширование на достаточно длительный срок (это позволит
значительно ускорить по крайней мере открытие последующих страниц на этом сайте). Все это делается несколькими строками в конфигурационном файле Apache ( или .htaccess ):
AddOutputFilterByType DEFLATE text/html AddOutputFilterByType DEFLATE text/xml AddOutputFilterByType DEFLATE image/x-icon AddOutputFilterByType DEFLATE text/css AddOutputFilterByType DEFLATE application/x-javascript BrowserMatch ^Mozilla/4 gzip-only-text/html BrowserMatch ^Mozilla/4\.0[678] no-gzip BrowserMatch Konqueror no-gzip BrowserMatch \bMSIE !no-gzip !gzip-only-text/html Header append Vary User-Agent <FilesMatch .*\.(css|js|php|phtml|shtml|html|xml="")$> Header append Cache-Control private </FilesMatch> ExpiresActive On ExpiresDefault "access plus 1 month" <FilesMatch .*\.(shtml|html|phtml|php="")$> ExpiresActive Off </FilesMatch>
Данный фрагмент кода включает сжатие для тех типов файлов, для которых это разумно сделать, затем отключает сжатие для старых и неподдерживаемых браузеров. Заголовки
Объединение и минимизация файлов может быть достаточно проблематичным занятием, но для этой цели можно использовать пакетную оптимизацию (http://webo.in/my/action/packet/), загрузить один архив со всеми файлами, которые требовалось оптимизировать, — и на выходе получить уже готовые к применению версии.
Следующим шагом по соотношению "эффективность/сложность" будет работа с изображениями. Начать лучше всего с создания CSS
Попутно все GIF-изображения переводятся в PNG-формат. Для этого архив с изображениями прогоняется через всю пакетную оптимизацию. Отдельным пунктом для сайта www.vacLavak.ru стоит упомянуть работу с ани-мированными GIF. В исходной точке все банеры занимали порядка 180 Кб. При оптимизации повезло (удалось удалить ненужные кадры и немного уменьшить палитру), в итоге размер рекламного блока сократился вдвое.
В результате проведенных действий размер страницы уменьшился до 350 Кб, что было бы совсем неплохо, учитывая изначально "тяжелый" дизайн сайта.
Однако этого оказалось недостаточно.
Для более точного анализа реальной картины можно воспользоваться счетчиком времени загрузки (более подробно он описан в практическом приложении в книге "Разгони свой сайт"), который замеряет полное время загрузки у всех посетителей сайта для произвольной страницы. Установка его чрезвычайно проста: нужно получить код, установить его максимально близко к началу страницы в шаблоне (идеально — сразу после title) и пару дней собирать статистику (зависит от числа хитов; если на сайт заходит несколько десятков тысяч ежедневно, то хватит и дня или пары часов).
(рис 8.6) Скорость загрузки страниц на четвертом этапе клиентской оптимизации для www.vaclavak.ru, источник: webo.inПосле установки счетчика обнаружилась следующая картина: среднее время загрузки (уже оптимизированного) сайта составило 16 секунд, за 4 секунды страницы загружались у 58% пользователей.
Было решено существенно переработать логику загрузки страницы: вынести рекламные блоки в пост-загрузку, а загрузку самого JavaScript "отложить". Все это было сделано в лучших традициях "ненавязчивого"
JavaScript, которые описаны в седьмой главе книги "Разгони свой сайт". OnDOMReady (для точности измерения посещений), остальные — на window.onload.
В результате на странице сразу загружалось только основное содержание. Сразу после него —
(рис 8.7) Скорость загрузки страниц на пятом этапе клиентской оптимизации для www.vaclavak.ru, источник: webo.inЕдинственная сложность возникла с блоком валют — он вызывался через внешний JavaScript-файл, который в свою очередь содержал docu-ment.write. Его пришлось вставлять через динамический iframe, который получал данные, а потом отправлял родительской странице. Поскольку информер этот загружается достаточно медленно, получился значительный выигрыш во времени загрузки страницы
после его вынесения в постзагрузку.
После очередных замеров с помощью счетчика времени загрузки получилась следующая картина: среднее время загрузки — 7,3 секунды (ускорение более 100% от уже оптимизированного варианта), за 4 секунды сайт загрузился у 82% посетителей.
Благодаря существованию большого количества удобных инструментов для клиентской оптимизации (на http://webo.in/) удалось очень оперативно проработать клиентскую составляющую обычного в общем-то сайта. Только для широкополосного подключения скорость загрузки увеличилась в 7 раз (для коммутируемого доступа это число еще более значительно в связи с существенным уменьшением числа запросов, необходимых для первичного отображения страницы).
(рис 8.8) История применения клиентской оптимизации для www.vaclavak.ruСам процесс оптимизации относительно оценки самого webo.in мож- но представить следующим образом (в этом уже может помочь история проверок сайта, http://webo.in/my/action/history/):
Возможно, не все из описанных шагов необходимы для среднестати стического сайта, рассчитанного на рядовых пользователей Интернета, но сам процесс оптимизации (от простого к сложному, от более эффективного — к менее эффективному) должен, по-видимому, протекать именно таким образом.
Этот раздел написан под впечатлением статьи Viktar Karpach YSlow and ASP.NET: 100 points "A"
(рис 8.1) Измерения скорости загрузки сайта при помощи YSlow. Источник: www.karpach.comВо-первых, нужно объединить в один файл таблицы стилей и файлы скриптов. Это стоит сделать только для рабочего (не тестового) выпуска сайта при помощи MS BuiLd:
<ItemGroup> <TextFiles Include="*.css" Exclude="global.css"/> </ItemGroup> <Exec Command="echo y| type %(TextFiles.Identity) >> global.css"/>
Таким образом мы можем объединить все CSS- и JS-файлы. Некоторые разработчики зададут резонный вопрос: а что можно сказать по поводу WebResource.axd? В новом AJAX ControL TooLkit есть TooLkitScriptManager, который позволяет объединить большинство ваших файлов WebResource.axd. Также он может применять к ним gzip-сжатие (это будет важно для следующих разделов).
Затем нам нужно создать CSS
Сети доставки содержания (англ.
Можно также применить msbuild для изменения файлов стилей при
использовании
<Import Project=".\References\MSBuild.Community.Tasks.targets" /> <Target Name="Release"> <FileUpdate Files="$(OutputPath)styles\basic.css" Regex="\.\.\/images/(["\)]*)" ReplacementText= "http:=""//karpach.appspot.com/cdn/images/$1" /> </Target>
Теперь нужно положить в
Однако, по всей видимости, этот подход может быть неприменим
для файлов стилей. Для начала стоит обеспечить контроль изменений
версий таблиц стилей (в данном случае это
Движок Google Apps позволяет легко реализовывать
- url: /cdn/styles/basic_\d*\.css static_files: cdn/styles/basic.css upload: cdn/styles/basic\.css
Более подробно о настройке
У всех файлов, расположенных в
private readonly static string[] CACHED_FILE_TYPES = new
string[] { ".jpg", ".gif", ".png",".css" };
public void Init(HttpApplication context)
{
context.AcquireRequestState += new
EventHandler(context_AcquireRequestState);
}
void context_AcquireRequestState(object sender, EventArgs e)
{
HttpContext context = HttpContext.Current;
if (context != null context.Response != null)
{
string fileExtension = Path.GetExtension
(context.Request.PhysicalPath).ToLower();
if (context.Response.Cache != null
Array.BinarySearch<string>(CACHED_FILE_TYPES, fileExtension)
>= 0)
{
HttpCachePolicy cache = context.Response.Cache;
TimeSpan duration = TimeSpan.FromDays(365);
cache.SetCacheability(HttpCacheability.Public);
cache.SetExpires(DateTime.Now.Add(duration));
cache.SetValidUntilExpires(true);
cache.SetNoServerCaching();
cache.SetMaxAge(duration);
}
}
}
В принципе, у вас вообще не должно быть этого модуля с учетом то-
го, что все статические файлы должны быть расположены в
Это делается очень просто. Просто надо ввести эти действия в привычку. Помните об этом при разработке модулей на стороне сервера и используйте коллекцию Header.Controls для добавления таблиц стилей на страницу.
Следовать этому правилу бывает очень сложно. Например, иногда хочется добавить
Library.js
может содержать
function DoSomething()
{
}
А после этого прямо в коде страницы:
<script type='text/javascript'>
if (typeof (DoSomething) == 'undefined')
{
alert('Library is not loaded yet');
}
</script>
Приведенный пример будет недостаточно верным, потому что загружаемая функция может быть доступна на странице после некоторого времени. Лучше всего создавать дополнительную клиентскую логику, использующую определенный модуль, в самом этом модуле либо проверять через равные промежутки времени, доступен ли необходимый функционал. Более подробно данные техники были освещены в лекции "Ненавязчивый JavaScript" книги "Разгони свой сайт".
Для этого стоит скопировать внешние ресурсы (картинки, JavaScript файлы) к себе на сайт.
Например, на блоге располагается иконка валидной страницы от W3.Изначально эта иконка находилась на сайте W3 Schools, но для ускорения загрузки сайта стоит скопировать ее в свой проект. Тогда она будет располагаться локально, а при загрузке сайта понадобится на 1 DNS-запрос меньше.
Минимизацию JS-кода можно осуществлять при публикации очередного выпуска сайта при помощи MS Build (это наиболее разумная позиция — осуществлять все оптимизационные процедуры при превращении сайта из
тестового в рабочий). Для этой цели можно использовать YUI
<Target Name="Compress"> <Message Text="Create temp files ..." /> <Copy SourceFiles=".\$(ProjectName)\Javascript\ColorPicker.js" DestinationFiles=".\$(ProjectName)\Javascript\ColorPicker.js.full"/> <Copy SourceFiles=".\$(ProjectName)\Styles\ColorPicker.css" DestinationFiles=".\$(ProjectName)\Styles\ColorPicker.css.full"/> <Exec Command="java -jar yuicompressor-2.4.2.jar —type js .\$(ProjectName)\Javascript\ColorPicker.js.full >.\$(ProjectName)\Javascript\ColorPicker.js"/> <Exec Command="java -jar yuicompressor-2.4.2.jar —type css .\$(ProjectName)\Styles\ColorPicker.css.full >.\$(ProjectName)\Styles\ColorPicker.css"/> </Target>
Аккуратно подходите к разработке своего сервера и пользовательских расширений. Проверяйте, что несколько различных расширений загружают общие библиотеки только один раз.
Кэширующий модуль и ETag:
Response.Cache.SetETag
Данный раздел подготовлен при помощи Елены Цаплиной (
Елена известна своей публикацией о
В данный момент под ее руководством (на базе студии Aquanther) разрабатывается мультимедийный курс по созданию и управлению сайтом с использованием
DrupaL — довольная распространенная
При создании сайта используем DrupaL версии 6, так как в нем лучше реализованы внутренние средства кэширования. Также в дополнительных модулях (Views, PaneL и т. д.) для DrupaL версии 6 внедрены эффективные методы кэширования. К сожалению не все модули DrupaL версии 5 реализованы для DrupaL версии 6 (например, модуль Sphinx), о чем не следует забывать при планировании разработки Интернет-сайта. Далее будем рассматривать только DrupaL версии 6.
Хорошо обдумаем варианты использования модулей наподобие
Вариант второй, с использованием
Кэширование системы меню, фильтров форматов ввода, переменных администрирования (например, название сайта) и настроек модуля производится автоматически. Остальные параметры кэширования можно настроить на странице "Управление — Производительность" ( http://www.exampLe.ru/admin/settings/performance )
На данной странице можно настроить:
Включим кэширование страниц в режим "нормальный". В данном режиме кэширования страниц при просмотре страницы в первый раз (анонимным, незарегистрированным в системе пользователем) производится сохранение сгенерированной страницы в кэш. В дальнейшем при просмотре данной страницы (анонимным пользователем) она не генерируется заново, а берется из кэша, что значительно ускоряет работу Drupal.
Если кэширование страниц включено в режиме "агрессивный", при генерации страницы пропускается загрузка и выгрузка включенных модулей, поэтому часть модулей могут работать некорректно или не работать совсем.
(рис 8.2) Настройки производительности для DrupalНастроим минимальное время жизни кэша страниц для анонимных пользователей. Данный параметр определяет, через какое время после кэширования страницы производится проверка на то, обновлено ли содержимое данной страницы или нет. Если обновлено, то кэш данной страницы очищается. Т.е. если администратор сайта изменил содержимое страницы, он его увидит сразу, а анонимные пользователи — только по прошествии минимального времени жизни кэша.
Включим компрессию страниц для сохранения сжатого кэша страниц и для передачи страницы браузеру пользователя в сжатом виде, если он поддерживает компрессию gzip. Компрессия производится с помощью библиотеки zLib, установленной как расширение в PHP.
Включим кэширование блоков. Принцип работы кэширования блоков аналогичен принципу кэширования страниц. Для супер-пользователя (первого зарегистрированного пользователя при установке DrupaL, его id равен 1) блоки никогда не кэшируются.
Включим оптимизацию CSS- и JavaScript-файлов. Это уменьшит их размер и количество обращений к серверу при загрузке страниц в браузер. Все CSS- и JavaScript-файлы собираются в один (свой файл для CSS (обычно их бывает два: один для отображения на экране, другой — для отображения при печати) и свой — для JavaScript). Таким образом мы уменьшим количество обращений к серверу при загрузке страницы.
Authcache сохраняет сжатый кэш страниц отдельно для каждого пользователя или роли. Кэш сохраняется в базе данных или в стороннем средстве кэширования (memcahed,
Для установки модуля:
/sites/all/modules ;index.php )/sites/default ) и добавим
следующий код (без примечаний за // ...) в начало файла после
тега <?php:$conf['cache_inc'] = './sites/all/modules/authcache/api/authcache.inc'; $conf['authcache'] = array( 'default' => array( // технология кэширования - apc, memcache, db, file, // eacc or xcache 'engine' => 'db', // если используем memcached (host:port, например, // 'localhost:11211') 'server' => array(), // если используем процесс memcached, shared или single 'shared' => TRUE, // кэш ключа префикса (для нескольких сайтов) 'prefix' => '', // если используем кэширование на файлах — указываем // путь их сохранения 'path' => 'files/filecache', // статический массив кэша (расширенный) 'static' => FALSE, ), );
В данном коде устанавливаются настройки модуля Authcache. Указываем, что будем хранить кэш страниц в базе данных ('engine' => 'db'), поэтому все остальные установки не имеют значения, и мы оставляем их без изменений. Более подробно о параметрах данного кода можно прочитать на странице http://drupaL.org/project/cacherouter (на английском языке).
Включим модуль Authcache на странице "Управление — Модули" ( http://www.exampLe.ru/admin/buiLd/moduLes ). После чего настроим его работу на странице "Управление — Производительность — Authcache" ( http://www.exampLe.ru/admin/settings/performance/authcache ):
Invalidate all user sessions ) при первом запуске;Save clear cached pages ) для сохранения изменений.
(рис 8.3) Настройка модуля AuthcacheИзменим настройки элементов на страницах сайта так, чтобы страница для разных пользователей, принадлежащих одной роли, выглядела одинаково (т. е., например, запретим пользователям управлять видимостью блоков на сайте, если такие блоки существуют).
В шаблонах тем оформления используем переменные:
$user_name - для отображения имени аутентифицированного пользователя;$user_link - для отображения ссылок, связанных с профилем пользователя;$is_page_authcache — если установлен в TRUE, то все хуки данного шаблона темы оформления будут сохранены в кэш.Можно также ознакомиться с примером /sites/all/modules/authcache/modules/authcache_example, который показывает, как настроить блоки с пользовательским содержанием (с контентом пользователя).
Authcache и кэширует страницы
лучше встроенного кэширования Drupal. Скачаем модуль по адресу http://drupal.org/project/authcache . После скачивания распакуем модуль в
папку /sites/all/modules. Включим модуль Cache Router на странице "Управление — Модули"
(http://www.example.ru/admin/build/modules ).
Откроем файл settings.php (в папке /sites/default ) и добавим следующий код в начало файла перед тегом <?php:$conf['cache_inc'] = './sites/all/modules/cacherouter/cacherouter.inc'; $conf['cacherouter'] = array( 'default' => array( 'engine' => 'db', 'server' => array(), 'shared' => TRUE, 'prefix' => '', 'path' => 'sites/default/files/filecache', 'static' => FALSE, 'fast_cache' => TRUE, ), );
После осуществления действий, приведенных выше, страницы создаваемого сайта будут отдаваться сервером браузеру пользователя в сжатом виде, а вот CSS и JavaScript — нет. Исправим это:
/sites/all/modules ;"GZip CSS" и "GZip JavaScript" ;.htaccess, расположенный в корневой директории сайта (на основании данных из README.txt, входящего в состав модуля CSS Gzip), пропишем меду тегами <IfModule mod_rewrite.c> и </IfModule> следующий код:### START CSS GZIP ###
# Requires mod_mime to be enabled.
<IfModule mod_mime.c="">
# Send any files ending in .gz with x-gzip encoding
# in the header.
AddEncoding x-gzip .gz
</IfModule>
# Gzip compressed css files are of the type 'text/css'.
<FilesMatch "\.css\.gz$">
ForceType text/css
</FilesMatch>
<IfModule mod_rewrite.c="">
RewriteEngine on
# Serve gzip compressed css files
RewriteCond %{HTTP:Accept-encoding} gzip
RewriteCond %{REQUEST_FILENAME}\.gz -s
RewriteRule ^(.*)\.css $1\.css\.gz [L,QSA,T=text/css]
</IfModule>
### End CSS GZIP ###
Если в шаблонах темы оформления необходимо использовать дополнительные CSS и JavaScript, то желательно подключать их с помощью следующих команд:
drupal_add_css(‘путь к CSS относительно корневой директории сайта’);drupal_add_js(‘путь к JavaScript относительно корневой директории сайта’);для того, чтобы они оптимизировались (включались в один исходный CSS или JavaScript-файл) и сжимались совместно со всеми остальными CSS- или JavaScript-файлами, используемыми на сайте.
От оптимизации Drupal с помощью модулей, перейдем к более сложной оптимизации — оптимизации конфигурации и обслуживания Drupal.
/sites/default в файле settings.php изменим строкуini_set('session.gc_maxlifetime', 200000);
на
ini_set('session.gc_maxlifetime', 86400); // 24 часа (в секундах)
Также в этом файле можно сократить время жизни кэшированных страниц сеансов до 24 часов, изменив строку
ini_set('session.cache_expire', 200000);
на
ini_set('session.cache_expire', 1440); // 24 часа (в минутах)
Напоследок в этом же файле изменим время хранения cookie в браузере пользователя, сократив его до 24 часов:
ini_set('session.cookie_lifetime', 86400); // 24 часа (в секундах)
Если установить время хранения cookie в браузере пользователя равным 0, то cookie будет удаляться сразу после закрытия Интернет-браузера пользователем.
Так как сервер сайта может работать под управлением разных операционных систем:
то в каждом случае настройки оптимизации сервера будут отличаться (т.
е. установка eAccelerator в Windows и Lunux сильно различается). Ниже
приведены только основные рекомендации по оптимизации сервера. Подробно из рекомендаций рассмотрена лишь установка PHP-
sudo apt-get install php5-dev sudo apt-get install make
sudo cd /tmp/ sudo wget http://bart.eaccelerator.net/source/0.9.5.3/eaccelerator- 0.9.5.3.tar.bz2 sudo tar xvjf eaccelerator-0.9.5.3.tar.bz2 sudo cd eaccelerator-0.9.5.3 sudo phpize sudo ./configure —enable-eaccelerator=shared sudo make sudo make install
; eAccelerator configuration ; Note that eAccelerator may also be installed as a PHP extension or as a zend_extension ; If you are using a thread safe build of PHP you must use ; zend_extension_ts instead of zend_extension ;extension = "/usr/lib/php5/20060613+lfs/eaccelerator.so" zend_extension = "/usr/lib/php5/20060613+lfs/eaccelerator.so" eaccelerator.shm_size = "16" eaccelerator.cache_dir = "/var/cache/eaccelerator" eaccelerator.enable = "1" eaccelerator.optimizer = "1" eaccelerator.check_mtime = "1" eaccelerator.debug = "0" eaccelerator.filter = "" eaccelerator.shm_max = "0" eaccelerator.shm_ttl = "0" eaccelerator.shm_prune_period = "0" eaccelerator.shm_only = "0" eaccelerator.compress = "1" eaccelerator.compress_level = "9" eaccelerator.allowed_admin_path = "/var/www/eaccelerator"
; eAccelerator configuration ; Note that eAccelerator may also be installed as a PHP extension or as a zend_extension ; If you are using a thread safe build of PHP you must use ; zend_extension_ts instead of zend_extension ;extension = "/usr/lib/php5/20060613+lfs/eaccelerator.so" zend_extension = "/usr/lib/php5/20060613+lfs/eaccelerator.so" eaccelerator.shm_size = "16" eaccelerator.cache_dir = "/var/cache/eaccelerator" eaccelerator.enable = "1" eaccelerator.optimizer = "1" eaccelerator.check_mtime = "1" eaccelerator.debug = "0" eaccelerator.filter = "" eaccelerator.shm_max = "0" eaccelerator.shm_ttl = "0" eaccelerator.shm_prune_period = "0" eaccelerator.shm_only = "0" eaccelerator.compress = "1" eaccelerator.compress_level = "9" eaccelerator.allowed_admin_path = "/var/www/eaccelerator" ; ionCube Loader configuration zend_extension=/usr/local/lib/ioncube/ioncube_loader_lin_5.2.so ; Zend Optimizer configuration zend_extension=/usr/local/lib/Zend/ZendOptimizer.so zend_optimizer.optimization_level=15
sudo mkdir -p /var/cache/eaccelerator sudo chmod 0777 /var/cache/eaccelerator
sudo /etc/init.d/apache2 restart
mod_expires , который позволяет Drupal посылать HTTP-заголовки Expires , кэшируя все статические файлы
(изображения, CSS, JavaScript и т. п.) в Интернет-браузере пользователя на определенный срок или до момента появления новых версий файлов. Настройки взаимодействия
Drupal и модуля mod_expires веб-сервера Apache находятся в файле .htaccess в корневой директории сайта:# Включить mod_expires. <IfModule mod_expires.c=""> # Разрешить истечение срока. ExpiresActive On # Кэшировать все файлы на две недели после доступа (A). ExpiresDefault A1209600 # Не кэшировать динамически генерируемые страницы. ExpiresByType text/html A1 </IfModule>
.htaccess файлов веб-сервером их содержание можно перенести в главный файл конфигурации Apache — .htaccess в
пределах корневого каталога веб-сервера, установив AllowOverride в None:<Directory/> AllowOverride … </Directory>
.htaccess в httpd.conf не пропустить не один файл .htaccess.my.cnf в папке /etc/mysql (при использовании Ubuntu 8.04). Изменим значение# query_cache_limit = 1M # query_cache_size = 16M
на
query_cache_limit = 1M query_cache_size = 64M
После чего перезапустим MySQL командой
/etc/init.d/mysql restart
Слишком маленький размер кэша — малоэффективен, а слишком больой размер кэша приводит к тому, что поиск нужной информации в кэше тратит много времени. Поэтому рекомендуем поэкспериментировать с размером кэша на каждом конкретном сервере и подобрать его оптимальный размер.
settings.php (в папке /sites/default ) — либо разместим сайт на кластере
из серверов (например, воспользовавшись услугами сервиса Amazon C2).Для просмотра информации о сервере из Drupal существует удобный модуль — System information (http://drupal.org/project/systeminfo ). После его установки и активации информацию о вашем сервере можно посмотреть на странице http://www.example.ru/admin/reports/systeminfo.
Ускорение работы любой системы возможно, в основном, за счет кэширования некоторых (тут стоит подчеркнуть, что именно некоторых, а не всех подряд) часто используемых операций. Все кэширующие мероприятия, в том числе и для Wordpress, можно разбить на несколько основных частей:
Данную проблему можно проиллюстрировать при помощи следующего рисунка:
Так уж сложилось, что основное узкое место практически любой системы заключается в базе данных, поэтому ее стараются ускорить всеми возможными способами. Стоит отметить, что проблема многочисленных вызовов к
базе данных не решается просто уменьшением их количества (для предоставления той же самой информации), тут надо подходить более комплексно и настраивать многоуровневый кэш для запросов. Относительно
MySQL это сделать довольно просто: достаточно прописать в конфигурационном файле my. (или my.ini ) следующие параметры (в случае большого количества оперативной памяти 20 Мб
может быть увеличено до любого
приемлемого количества, но не стоит здесь увлекаться: скорость поиска данных в кэше напрямую зависит от размера самого кэша):
(рис 8.4) Кэширующие звенья для Wordpress, источник: www.arnebrachhoLd.dequery-cache-type=1 query-cache-size=20M
Для оптимизации таблиц (что позволит уменьшить время запросов на 20—50%) можно воспользоваться дополнением Optimize DB
(http://yoast.com/wordpress/optimize-db/ ), которое позволит существенно уменьшить размер таблиц MySQL и улучшить их структуру.
Для кэширования запросов к базе данных также существует специальное дополнение, DB Cache
Каждый раз, когда исполняется
Следующим шагом для борьбы с большим временем подготовки страницы на сервере будет полное кэширование создаваемой страницы в один файл или одну запись в оперативной памяти. Для включения внутреннего кэширования на
уровне самого Wordpress достаточно раскомментировать (или добавить) в файл wp-config.php следующие строки (предварительно проверив, что директория wp-content/cache доступна для записи, иначе
ничего не получится):
define('ENABLE_CACHE', true );
define('CACHE_EXPIRATION_TIME', 900);
Более серьезных результатов кэширования можно добиться при помощи дополнения WP-Super-Cache (http://ocaoimh.ie/wp-supercache/ , базирующегося на WP-Cache, http://mnm.uib.es/gallir/wpcache-2/) или Hyper Cache (http://www.satollo.com/english/wordpress/hyper-cache), которое вообще не будет осуществлять никаких запросов к базе данных для отображения внешних веб-страниц. Однако при этом станет невозможно учитывать статистику посещений через встроенные в Wordpress методы (только через внешние счетчики или по логам сервера). Для Wordpress, установленного на IIS, также лучше всего будет использовать именно WP-Super-Cache вместо IIS Output Caching. Это подробно рассматривается в соответствующей заметке; ниже приведено число запросов в секунду при том или ином методе серверного кэширования.
Но давайте посмотрим, что можно сделать с клиентской составляющей (дизайном и скриптами) обычного блога.
(рис 8.5) Рис. 8.5. Производительность кэширования Wordpress для IIS, источник: bLogs.iis.net
Основной минус богатства тем для Wordpress — то, что они в основном разрабатываются любителями. Как следствие, такие темы могут состоять из большого количества картинок и файлов стилей, которые в совокупности загружаются очень медленно. Однако ситуация поправимая. Для ускорения загрузки сайта в самом браузере (а это, по мнению экспертов Yahoo!, занимает 95% времени общей загрузки страницы) можно воспользоваться несколькими решениями:
.htaccess-файл (что обеспечивает более широкую совместимость и снимает нагрузку с В общем, даже самый обычный блог может быть ускорен в несколько (десятков) раз за считанные минуты. Скорее всего, в ближайшем будущем уже появятся отдельные сборки Wordpress, настроенные на максимальную производительность при задействовании любой темы и произвольной посещаемости блога. Сейчас же можно просто воспользоваться вышеприве- денными советами и порадоваться за значительное увеличение числа посещений и постоянных читателей.
Скорее всего, многие уже слышали о медленной работе JoomLa! (http://www.joomLa.org/), одной из самых популярных бесплатных
Естественно, что у такой простоты есть и обратная сторона: большинство расширений создаются без учета требований высокой производительности и какой-либо оглядки на мощности конечных серверов, очень часто даже не выделенных или виртуальных, а расположенных на общем хостинге. Разработка бесплатных решений оборачивается 100-500% замедлением в скорости загрузки сайта. Давайте разбираться, как с этим можно бороться.
Для начала посмотрим, что можно сделать с серверной производительностью. Мы исследовали стандартную сборку JoomLa!, но даже в такой комплектации на отдельном сервере время создания страницы занимало 0,312 с (замер времени ответа производился с помощью curl, интерфейс к которому выложен на webo.in,http://webo.in/my/action/timings/). Это не очень много, но в условиях виртуального хостинга может возрасти многократно, даже на изначально хорошо оптимизированных окружениях.
Встроенное кэширование в JoomLa! 1.5 работает достаточно хорошо и позволяет сэкономить существенное время при создании страницы. Однако нужно понимать, что оно может быть применимо далеко не для всякой системы. В случае статичных новостных сайтов и небольших интернет-магазинов кэширование может помочь, но для социальных сетей с активным добавлением новых материалов и их комментированием оно точно не подойдет.
Кэширование на стороне сервера
Включение встроенного кэширования в Joomla! 1.5 сократило время ответа тестируемого сервера примерно на 30% (на 0,107 с). Прекрасно понятно, что в большинстве случаев оно будет практически бесполезно: если необходимо сократить время создания страниц на порядок, то нужны более кардинальные методы.
В качестве одного из кэширующих решений может использоваться и Web Optimizer: встроенное кэширование HTML-документов позволяет отдавать их сразу в том виде, в котором они получаются системой после всех запросов к базе. При этом, естественно, практически все эти запросы не осуществляются. Данное кэширование ("монолитное") подойдет только в тех случаях, когда внешние страницы у Joomla! меняются относительно редко.
Если просто включить Web Optimizer ( http://code.google.com/p/weboptimizator/ ) в процесс создания страниц, то время обработки документа возрастет незначительно (после создания всех кэширующих файлов на 0,006 с или 3% на тестовом сервере). Дополнительно включив HTML-кэши- рование в Web Optimizer, можно сократить время отдачи документа до 0,08 с (почти в 4 раза по сравнению с исходным временем создания страницы). Сразу стоит отметить, что установка вроде бы аналогичного по функциональ- ности дополнения Content Static ( http://extensions.joomla.org/extensionssite-management/cache/5104/details/ ) визуально на производительности никак не отразилась.
Очевидно, что более грамотным будет кэшировать отдельные модули на странице, оставляя нужные места (или, как их любят называть в шаблонных движках, — заглушки) динамическими. Однако данное решение требует существенного
вмешательства в алгоритм работы самой
Заканчивая речь про кэширование создаваемых страниц на стороне сервера, стоит упомянуть, что дополнение JoomLa Performance
Кэширование запросов к базе данных
К серверному кэшированию можно подойти и с другой стороны: ограничить число запросов к базе данных, — обычно именно эта часть вызывает наиболее серьезную "утечку" производительности. Для JoomLa! существует расширение, позволяющее закэшировать все (или почти все) запросы к базе. Тут стоит понимать, что база данных сама по себе может работать достаточно быстро, и подобное решение будет эффективно только в том случае, если восстановление закэшированного значения выборки на порядок (или хотя бы в разы) быстрее, чем осуществление самой выборки (например, 1 мс против 10 мс). В противном случае прироста производительности не произойдет.
Для оценки эффективности решений для клиентской оптимизации использовалось хорошо зарекомендовавшее себя (и относительно беспристрастное) дополнение к Firefox — YSLow.
"Чистая" система
"Чистая" установка Joomla! 1.5 набрала 65 баллов из 100. Вполне приемлемо. Стоит понимать, что если на систему дополнительно поставить десяток модулей и компонентов, то оценка резко ухудшится до 30-40.
Следующий этап: архивирование
В Joomla! есть встроенный gzip. Однако, во-первых, он работает через PHP, во-вторых, только для HTML-файлов. Грустно, что и видно по оценке: она поднялась только до 67.
CssJsCompress
Довольно известное дополнение (http://extensions.joomla.org/extensions/site-management/cache/7350/details), позволяющее объединять CSS- и JS-файлы. Однако оно не добавляет к ним всех кэширующих заголовков и сжатия, что и отразилось на результате: всего 72 балла по YSlow. В самой Joomla! gzip при этом был включен. Дополнение CSS/JS Cache ( http://extensions.joomla.org/extensions/site-management/cache/7801/details) не удалось заставить корректно работать."
Joomla Perfomance Booster
Однако данное дополнение возможно подключить вместе с приложением Web Optimizer (которое возьмет на себя всю логику преобразования клиентской части), что позволит существенно ускорить работу сайта на Joomla! практически любой сложности.
Smart Optimizer
Далее был протестирован Smart Optimizer (http://farhadi.ir/works/smartoptimizer, как отдельное PHP-приложение) — по характеру работы полностью аналогичный известному Minify (http://code.google.com/p/minify/, дополнение Minify4Joomla, http://extensions.joomla.org/extensions/site-management/cache/7183/details , "завести" не удалось). Установка у него достаточно сложная для непрофессионала, к тому же приходится править шаблоны вручную, нет возможности объединять файлы из разных директорий. Однако все остальное на высоте: оценка поднялась до 85. В самой Joomla! gzip при этом был включен."
Web Optimizer
На данный момент для Joomla! 1.5 не существует более мощного бесплатного решения для оптимизации производительности, чем Web Optimizer. PHP Speedy (http://code.google.com/p/phpspeedy/), к сожалению, доступен только для Joomla! 1.0.
Материал для данного раздела и консультирование по вопросам производительности Joostina
предоставил Николай Кирш — веб-разработчик,
основатель и технический лидер проекта Joostina
Joostina родилась и развивается с изначальной целью: быть максимально быстрой и
эффективно использовать ресурсы сервера, не уменьшая при этом удобств как для пользователя, так и для администратора сайта. В основе системы лежит
Оптимизацию
Все настройки указаны для Joostina версии 1.3.0.
Самый быстрый и безопасный способ настроить свой сайт на более высокую скорость и выжать из него максимум возможностей — основательно ознакомиться с настройками, располагающимися в "Глобальной конфигурации". Для доступа к настройкам необходимо авторизоваться с правами Супер-администратора в административной части, называемой так же панелью управления: http://www.exampLe.ru/administrator. Далее надо выбрать пункт меню "Сайт — Глобальная конфигурация", или прямо на главной страницы панели управления, через кнопку быстрого доступа "Глобальная конфигурация".
Отключить генерацию RSS (syndicate)
Joostina, как и большинство современных
<link rel="alternate" type="application/rss+xml" title="Joostina v 1.3.0 b" href="http://www.example.ru/index2.php?option=com_rssfeed=0amp;no_html=1" />
Но формирование ссылки на ленту занимает определенное время, оно нужно на запрос в базу данных на получение параметров отображения. Если же тег ленты необходим, а избавиться от лишнего запроса тоже хочется, — можно прописать ссылку напрямую в шаблоне сайта.
Использовать шаблон
Для каждого пункта меню в панели управления можно выбрать уникальное отображение и состав модулей. Но также для каждого из пунктов меню можно назначить уникальный шаблон. Если такая возможность на сайте не используется, то ее следует отключить. Для этого и создана данная настройка. Параметр позволяет выбрать единый шаблон для всего сайта, что исключит один запрос и его обработку для выбора конкретного шаблона. Аналогичная настройка существует для панели управления.
Отключить мамботы группы system
Мамботы — это чаще всего небольшие PHP-сценарии, срабатываю щие на определенном этапе работы системы. Мамботы группы system сра- батывают в момент инициализации системы. Обычно в группу входят расширенные обработчики SEF и библиотеки подключения Javascript. Все мамботы этой группы можно посмотреть в панели управления "Меню - Мамботы -Мамботы сайта". Если справа в списке выбора типа нет группы system, то настройку рекомендуется отключить, это сделает ненужным один запрос в базу и инициализацию механизма.
Отключить мамботы группы content,
Отключить мамботы группы mainbody
Действие данного пункта аналогично группе system, но поступать тут надо внимательнее. Группа content — основная, за счет нее выводятся изображения, вставленные в текст через тег {mosimage},
разбивка на страницы внутри текста и т. д. Безопаснее всего поочередно снимать мамботы с публикации и смотреть, что изменилось на сайте. Если все мамботы не опубликованы, а сайт отображается верно, — можно
отключить всю группу.
Использовать неопубликованные мамботы
Мамботы группы content часто работают, заменяя определенные те-
ги в тексте, например, {mosimage}. Но если мамбот не опубликован, то система его все равно использует — чтобы убрать из текста этот самый тег {mosimage}. Если на сайте такие мамботы не используются, то лучше активировать данную настройку, исключив неиспользуемые обращения к
базе данных и подключение лишних файлов.
Авторизация на сайте
Отключив данный пункт, вы запретите инициализацию пользователей на сайте. Параметр запретит заведение пользовательских сессий в базе данных и позволит более полно кэшироваться страницам. Если на сайте не предусмотрена работа пользователей, то и авторизацию лучше отключить.
Время существования сессии на фронте
При авторизации для пользователя заводится специальная сессия, данные о ней записываются в базу данных и имеют определенный срок жизни: пока сессия жива — пользователь считается авторизованным. Если указать в настройке большое время жизни сессии, то в базе данных сессий будет довольно много значений, что повлечет дополнительную на грузку для поиска сессий конкретного пользователя
Отключить сессии на фронте
При посещении сайта авторизованным пользователем или даже гостем для него запускается механизм инициализации сессий, что влечет за собой запись данных в базу, создание cookie у пользователя и постоянную проверку авторизации. Если авторизация на сайте не важна, то параметр рекомендуется отключить. Но учтите, что модуль отображающих посетителей будет выдавать не точную информацию, так как он основывается на данных записанных в таблице сессий. Параметр рекомендуется использовать совместно с пунктом "Авторизация на сайте".
Отключить контроль доступа к содержимому
Хотя в Joostina имеется не очень много возможностей для полноценного создания и управления правами пользователей, доступ к содержимому всегда ведется с учетом прав текущего пользователя. Это добавляет в SQL-запрос дополнительное условие. На сайтах, где доступ не разграничен на зарегистрированных и гостей, параметр лучше активировать.
Считать число прочтений содержимого
При прочтении каждого содержимого увеличивается значение поля
счетчика в таблице содержимого. Постоянные изменения даже одного поля таблицы содержимого сводят на нет встроенный в mysql механизм кэширования, да и дополнительный запрос в базу тоже лучше исключить.
Настройку рекомендуется выключить, а ведение статистики доверить специализированным сервисам, типа li.ru или
Отключить проверки публикации по датам
Для каждого материала при редактировании можно указать определенные периоды начала и окончания публикации. Проверка по датам добавляет в каждый SQL-запрос условие соответствия текущей дате, а настройка данное условие отключает. Чем меньше условий в запросе, тем легче его будет отработать базе данных и тем быстрее пользователь сайта увидит ожидаемые страницы.
GZIP-сжатие страниц
Позволяет передавать пользователю более компактные страницы. Содержимое пакуется через gzip-алгоритм на сервере, а распаковывается автоматически в браузере пользователя. Получается экономия на трафике, но немного больший расход ресурсов сервера; более подробно расчет оптимальности применения gzip приведен во второй главе книги "Разгони свой сайт".
Блокировка компонентов
Позволяет отключить прямой доступ к компонентам, набрав специальный адрес в браузере. Если на сайте имеется компонент, но он не используется, то настройку лучше активировать.
Рейтинг/Голосование
В базовой поставке системы имеется мамбот группы content, позволяющий выставлять рейтинг для каждого материала. Если такая возможность не требуется, рейтинг лучше отключить, — это исключит лишние проверки и инициализации.
Ежедневная оптимизация таблиц базы данных
Добавляет в работу сайта ежедневную оптимизацию всех таблиц базы данных через выполнение OPTIMIZE TABLE для каждой. Данная процедура уменьшает фрагментацию и производит общую оптимизацию таблиц встроенными средствами mysql.
Сжатие CSS- и JS-файлов
Позволяет выдавать вместо обычных JS- и CSS-файлов их упакованные аналоги. Экономит трафик и позволяет указать более длительное время кэширования. Работает только для встроенных файлов.
Значение тега revisit:
Позволяет указать параметр тега:
<meta name="revisit" content="10 days" />
который указывает, как часто сайт должен посещать
Включить кэширование
В Joostina встроен механизм, позволяющий кэшировать результаты выполнения ресурсоемких функций или инициализаций объектов. Использование кэширования позволяет уменьшить число запросов в базу, уменьшить число подключаемых файлов и увеличить общую скорость работы системы. Кэширование лучше активировать после полного построения и отладки сайта.
Тип кэширующей системы
Кэшировать можно как в файлы, так и в специальные
Оптимизация кэширования
При активации параметра из кэшируемых объектов будут удалены все неиспользуемые символы, например, переводы строк или множественные пробелы. Это уменьшает общий объем файла кэша и немного уменьшает трафик, передаваемый пользователю.
Автоматическая очистка каталога кэша
При кэшировании в файлы каталог кэша может содержать множество просроченных объектов. Система следит, чтобы таких случаев не было, но для большей уверенности рекомендуется активировать и этот параметр. На оптимизацию работы сайта число файлов в каталоге кэша имеет прямое влияние: чем больше файлов в каталоге одного уровня — тем дольше файловая система будет их отдавать.
Кэширование меню панели управления
При работе в панели управления в верхней части отображается меню, которое содержит пункты для доступа к основным операциям. Меню частично формируется из базы данных. Например, список установленных компонентов или список разделов и категорий. Такие данные изменяются не очень часто, и лучше произвести кэширование этого участка. Активация параметра также сделает вывод меню через внешний JavaScript- файл, код которого исключится из тела страниц и будет кэшироваться еще и на стороне пользователя — в браузере.
Каталог кэша (/dev/shm)
По умолчанию файлы кэша складываются в каталог /cache в корне
сайта. В зависимости от настроек сервера можно попытаться перенести
этот каталог в более быстрое место, например, на диск с другой файловой
системой или /dev/shm. Не забудьте убедиться, что PHP-интерпретатор
имеет полный доступ к указанному каталогу
Время жизни кэша
Позволяет указать период времени, на который должно срабатывать встроенное кэширование. Если сайт имеет не очень частое изменение структуры и содержимого или слабую активность пользователей, то время лучше указать больше. Выбранный период используется по умолчанию для всего кэша. Но в настройках модулей можно дополнительно выбрать, на какой период их кэшировать.
Включить сбор статистики
Параметр отвечает за исключение из работы системы сбора информации о браузере и других данных, которые лучше собирать через специальные сервисы, озвученные выше. Рекомендуется отключить.
Вести статистику просмотра содержимого по дате
Аналогично предыдущему параметру — лучше отключить и вести все учеты на серверах специальных сервисов.
Статистика поисковых запросов
Joostina сохраняет информацию о каждом слове, которые пользователи ищут на сайте. Возможность позволяет частично проанализировать пользовательскую аудиторию и узнать их интересы. Если такой функционал не требуется — лучше отключить.
Один из основных принципов оптимизации — использование только необходимого функционала. Joostina по своей сути является универсальной системой, и это влечет за собой некоторую ограниченность и сложность. Базовый дистрибутив системы имеет набор встроенных расширений, часть которых может не использоваться на сайте. Всего в Joostina можно отключить расширения всех 3-х типов:
Отключение компонентов лишь косвенно влияет на оптимизацию
сайта. Но блокировка доступа — это отличный способ уменьшить число
страниц для индексации или попыток спама. Чем меньше страниц, тем быстрее их проиндексирует
Отключить неиспользуемые компоненты можно в панели управления на странице управления компонентами: "Меню - Компоненты - Управление компонентами". Для работы данного механизма необходимо, чтобы в глобальной конфигурации была активирована настройка "Блокировка компонентов".
Отключение модулей позволяет уменьшить чисто ненужных запросов в базу, подключение неиспользуемых файлов и общий расход памяти, выделяемой на генерацию страницы. Выяснить, какие модули действительно необходимы, можно по следующей схеме.
Заходим в меню управления модулями: "Меню - Модули - Модули сайта".
Выбираем все модули и нажимаем кнопку "Скрыть", находящуюся в верхней части страницы — на тулбаре.
В новой вкладке или новом окне открываем главную страницу сайта и поочередно публикуем нужные модули. Начать лучше с модуля главного меню. Перед публикацией каждого модуля подумайте, нужен ли он, и попутно запоминайте, сколько новых запросов прибавилось. Если какой-то из модулей создает слишком большую нагрузку — придется поискать его более простые аналоги или найти способы оптимизации кода.
Отключение мамботов позволяет существенно сократить непосредственное время генерации страницы. Мамботы разделены на группы, про это уже сообщалось ранее, и каждая группа
отвечает за отдельные участки. Наиболее часто используемые — мамботы группы content, они позволяют обрабатывать содержимое, выдаваемое компонентом. Чаще всего такие
мам-боты отвечают за замену в тексте специально оформленных тегов на необходимый функционал или оформление. Например, мамбот bot_mosimage отвечает за замену тега {mosimage}
на необходимую картинку. Но для такой работы производится обработка текста регулярными выражениями, что не очень хорошо сказывается на производительности. Отключить неиспользуемые мамботы можно по той же
схеме, что и модули, только на другой странице панели управления: Меню — Мамботы — Мамботы сайта.
Если все мамботы группы отключены, то лучше отключить и саму группу — это делается в глобальной конфигурации для каждой группы отдельно.
Уже много людей писали руководства, помогающие вашему веб-приложению работать быстрее. В этом разделе будут освещены самые простые, но наиболее эффективные методы, которые дадут вам возможность
существенно ускорить ваше приложение без потери какого-либо функционала из
Часто бывает так, что одно веб-приложение подгружает сразу несколько JavaScript-файлов и CSS-стилей. Это существенно замедляет загрузку страницы, так как веб-браузер каждый раз заново запрашивает новый файл.
Решение заключается в том, чтобы уменьшить количество внешних ресурсов на вашей странице, объединив их все в один файл. Поможет нам в этом плагин AssetPackager (http://synthesis.sbecker.net/pages/asset packager). Ставим
script/plugin install git://github.com/sbecker/asset_packager.git
Пример config/asset_packages.yml:
javascripts: - base: - prototype - effects - controls - dragdrop - application - secondary: - foo - bar stylesheets: - base: - screen - header - secondary: - foo - bar
И запускаем rake-задачу:
rake asset:packager:build_all
Дальше для JavaScript пишем
<%= javascript_include_merged :base %>
или
<%= javascript_include_merged 'prototype', 'effects', 'controls', 'dragdrop', 'application' % >
Для стилей пишем:
<%= stylesheet_link_merged :base %>
или
<%= stylesheet_link_merged 'screen', 'header' %>
В итоге получаем для режима разработки наш старый код, например:
<script type="text/javascript" src="/javascripts/prototype.js"></script> <script type="text/javascript" src="/javascripts/effects.js"></script> <script type="text/javascript" src="/javascripts/controls.js"></script> <script type="text/javascript" src="/javascripts/dragdrop.js"></script> <script type="text/javascript" src="/javascripts/application.js"></script> <link href="/stylesheets/screen.css" type="text/css" /> <link href="/stylesheets/header.css" type="text/css" />
А в режиме рабочего сайта будет:
<script type="text/javascript" src="/javascripts/base_packaged.js?123456789"></script> <link href="/stylesheets/base_packaged.css?123456789" type="text/css" />
Теперь, чтобы сделать нагрузку еще меньше, переносим все свои статические файлы на другой хост. В
config.action_controller.asset_host = "http://assets.example.ru"
Теперь все image_tag, javascript_include_tag и т. д. будут указывать на этот хост.
Материал для данного раздела получен при общении с Олегом Смирновым (
К сожалению, большая часть всех плагинов имеет код низкого качества, поэтому при установке нескольких таких плагинов страница начинает существенно подтормаживать. О том, как поправить код чужого плагина, чтобы избежать лишнего расхода памяти при сложных манипуляциях с DOM-деревом, как сделать анимацию плавной даже при анимировании нескольких элементов, а AJAX — быстрым, а также о том, как избежать некоторых скрытых багов, и будет этот раздел.
Стоит начать с функции $, она принимает два параметра: первый — селектор, второй — контекст. Хотя контекст обычно опускают, впоследствии будет показано, как им грамотно пользоваться.
Простой селект
Самый простой вариант — это выбор по id, имени тега и имени класса.
$("#id")
$("tag")
$(".class")
Не случайно они расположены именно в такой последовательности: они идут по сложности алгоритма выборки. В первом случае вызов функции эквивалентен вызову
document.getElementById("id");
Поскольку предполагается, что id уникальный, поиск проходит очень быстро, и если на странице есть два элемента с таким id, то найден будет только первый. Хотя в IE и тут сделали ошибку и до 7-й версии включительно в случае отсутствия элемента с таким id он вернет элемент, у которого совпадает атрибут name.
Во втором случае тоже все относительно просто:
document.getElementsByTagName("tag");
Получили все ноды с таким именем из документа, и все готово. И на
удивление никаких ошибок, если не учитывать, что при запросе getElementsByTagName("*") IE вернет и комментарии тоже.
В третьем случае, если есть возможность, работу перехватывает
document.getElementsByClassName("class");
(Таблицу поддержки этой функции в браузерах можно посмотреть на quirksmode.org)
Эту функцию поддерживают еще не все браузеры. Для остальных применяется совсем другой алгоритм: нужно получить абсолютно все ноды, потом обойти их циклом, проверяя имена классов, и если совпали, то добавить в массив результата.
var nodes = document.getElementsByTagName("*"), result = [];
for (var i=0; i<nodes.length ; i=""
if="" ( " " + (nodes=""[i=""].className=""
nodes=""[i=""].getAttribute=""
.indexOf=""("class") >-1)
result.push(nodes[i]);
}
Какой метод применять, определяется в самом начале при подключении библиотеки.
Селект через querySelectorAll
приходится обычно использовать намного более сложные конструкции. И для них в современных браузерах FireFox 3.0, Safari 3.2, Opera 9.5,
а также в IE8, появились функции querySelector и querySelectorAll.
Они, соответственно, предназначены для поиска одной или нескольких
нод по CSS3-селекторам. Если браузер клиента поддерживает эту функцию, то все, о чем написано в прошлом пункте, — отпадает, и поиск происходит через querySelectorAll.
$("#id .class tag")
В лучшем случае селектор будет обработан именно querySelectorAll, потому что он написан по правилам CSS3. Но такое
возможно не со всеми селекторами: visible.
$("#id .class tag:visible")
Такой селектор выдаст ошибку в функции querySelectorAll, и селектор будет перенаправлен в поисковый движок Sizzle, где строка будет разбита на простые селекторы и превратится, по сути, в несколько разных поисков, в котором каждым следующим контекстом является предыдущий селектор.
$(document).find("#id").find(".class").find("tag").filter(":visible")
Скорость этого метода поиска напрямую зависит от величины DOM дерева: чем оно больше, тем медленнее, — но ее можно значительно увеличить, написав селектор раздельно.
$("#id .class tag").filter(":visible")
При этом querySelectorAll выберет все ноды, а Sizzle разберется с ":visible".
По поводу псевдо-селекторов возникает также очень интересный вопрос: CSS3 поддерживает несколько видов псевдо-классов, такие как :nth-of-type/:nth-child/:parent/:not/:checked, querySelectorAll не поддерживает данный селектор, но эта реализация иногда отличается.
Для примера возьмем псевдо-класс : nth-of-type и выберем все четные дивы, а из них все нечетные.
document.querySelectorAll("div:nth-of-type(even):
nth-of-type(odd)")
// Safari/FireFox:0 IE/Opera:N/A
$("div:nth-of-type(even):nth-of-type(odd)");
// Safari/FireFox:0 IE/Opera:All
$("div:even:odd"); // All: вернут 1,5,9 дивы
Первых два примера работают одинаково и вернут либо 0, если отра-
ботала функция querySelectorAll (это касается первого примера), либо
все элементы, потому что их обработал Sizzle (это особенность реализа-
ции выражения ":"). Третий же вернет 1, 5, 9 и т. д. элементы, а значит,
селекторы отрабатывали в три прохода: сначала из всего DOM-дерева бы-
ли выбраны все дивы, потом из них были выбраны все нечетные, а потом
из оставшихся были выбраны все четные.
visible/:animated/:input/:header. Их
лучше выделять отдельно, поскольку они могут сильно замедлить выборку.
Так, например, было с селекторами :visible/:hidden в версии 1.2.6: для то-
го чтобы узнать, видимый это элемент или нет, надо было подняться до са-
мого верха по DOM-дереву, проверяя атрибуты display и visible каждого ро-
дителя (http://mabp.kiev.ua/2009/02/07/accelerates-selectors-in-jquery/).
$("div").filter(":visible")
Псевдо-классы, используемые для поиска элементов формы, такие, как :radio, тоже имеют некоторое преимущество, если не применяется querySelectorAll; в противном случае CSS3-селектор input[type=radio] работает быстрее.
Сложенный селект
Сложенный селект — это когда нам надо выбрать группу из двух или более разных селекторов, например, все дивы, у которых класс равен A, B и C.
Это можно сделать двумя способами:
$(".a,.b,.c")
выбрать все сразу
$(".a").add(".b").add(".c")
или по одному.
При этом, если задействована функция querySelectorAll, то первый способ быстрее второго в четыре раза, а если нет, то второй в два раза быстрее первого.
Если уже заговорили про классы, их можно искать как любые другие
атрибуты — например, если надо найти все классы, имена которых начинаются на "my", можно сделать так:
$("[class^=my]")
а не городить логику с использованием add, тем более что такой способ
поддерживается querySelectorAll (http://mabp.kiev.ua/2009/02/21/testingproductivity-jquery-selectors/).
Неправильный селект в контексте
$(‘#listItem’ + i, $(‘.myList’))
Рассмотрим подробнее: контекст — это то, где ищут селектор, значит, пример можно переписать в более наглядную, но менее читаемую форму:
$($(".myList")).find("#listItem")
При этом контекст от первого поиска будет являться document.
$($(".myList",document)).find("#listItem")
Еще раз перепишем согласно формуле
$($(document).find(".myList")).find("#listItem")
И наконец, раскроем скобки
$(document).find(".myList").find("#listItem")
Что же получается: мы выполняем дорогостоящую операцию поиска по имени класса (по всему DOM-дереву в худшем случае) для того, чтобы упростить и без того самую простую операцию поиска по id?
Правильный селект в контексте
Правильно делать "с точностью до наоборот". В контексте надо ука-
зывать id элемента.
$(".class",$("#id"))
Однако можно не передавать в контекст
$(".class","#id")
Это можно переписать как
$("#id").find(".class")
Можно еще больше ускорить работу, если искать вот таким способом:
$(document.getElementById("id")).find(".class")
Но это, скорее всего, будет уже дурным тоном. Хотя поэкспериментировать интересно: что, если вместо getElementById взять querySelectorAll?
$("div",document.querySelectorAll("#id"))
Это примерно то же самое, что и
$("div",[document.getElementById("id")])
Ни прироста производительности, ни красоты кода из этого не получить, поэтому советую в контекст передавать что-то простое вроде id или при использовании псевдо-селекторов, обрабатываемых Sizzle’ом, пере давать их в селектор а все остальное в контекст:
$(":visible","input[type=checkbox]")
Раз уже пошла речь о псевдо-селекторах, то
$(":checkbox")
быстрее чем
$("input[type=checkbox]")
без использования querySelectorAll и наоборот.
Cложный селект
Часто возникает задача найти всех потомков одного родителя. Если нам известно, что все потомки являются непосредственными, то есть детьми, на этом можно сэкономить. Конечно же, лучше всего было бы написать правильный селектор
$("#id > div")
Но если выборка уже есть, то будем использовать ее как контекст. Как мы уже выяснили, поиск в контексте происходит при помощи функции find:
$("#id").find("> div")
Но find — очень дорогая функция, она просматривает абсолютно всех потомков контекста, поэтому лучше применить функцию children, она просматривает только непосредственных потомков.
$("#id").children("div")
Есть еще ряд функций поиска и манипуляций, которых стоит избегать
без крайней необходимости, — это find, closest, wrap, wrapInner,
replaceWith, clone. Стоит заметить, что wrapAll сюда не входит
(http://mabp.kiev.ua/2009/03/29/jquery-profiling/).
Внутреннее кэширование
Кэш у
Ситуация следующая: вы работаете со списком, у вас есть один из элементов li. Для того, чтобы получить все элементы, включая текущий, надо выбрать всех братьев (все, у кого родитель —
это родитель текущего) этого элемента и добавить его самого:
$("#id").siblings().add("#id")
Так как он — прошлый элемент, с которым работали в цепочке вызовов, мы можем взять его из КЭШа:
$("#id").siblings().andSelf()
Конечно, в данном конкретном случаи быстрее было бы сделать
$("#id").parent().children()
Потому что
Второй пример использования кэша — это простой возврат к предыдущей выборке, вместо того чтобы размазывать код на три строчки:
var elt = $("#id");
elt.children().css({/**/})
elt.click();
Можно после работы с детьми вернуться обратно к родителю и работать с ним дальше:
$("#id").children().css({/**/}).end().click()
Кэширование селекторов
Поскольку кэш так слабо развит, селекторы нужно кэшировать вручную. Давайте рассмотрим, например, вот такой код:
for(var i=0;i<1000;i
$("ul").append=""("<li>"+i""/
Все работает и выглядит красиво, но и это можно оптимизировать. Если вынести выборку за пределы цикла, добавление новых элементов будет проходить быстрее:
var elts = $("ul");
for(var i=0;i<1000;i
elts.append=""("<li>"+i+"</li>")
Буферизация
Но этот код можно заставить работать еще быстрее! Каждый раз, делая append, мы заставляем обновиться DOM-дерево и заставляем браузер перерисовать страницу. Этого можно избежать, придерживая вставку в DOM-дерево.
var str = "";
for(var i=0;i<1000;i
str += ""<li>"+i+"</li>"
$("ul").html(str);
Дело в том, что функции для работы с DOM-деревом у html-ноды, на которые
повешены события через
html и text вызывают функции полной очистки и только потом вставки нового
содержимого:
jQuery(DOMElement).empty().append(text)
Функция empty выбирает все ноды и по очереди удаляет:
jQuery(DOMElement).children().remove()
А функция remove уже заботится, чтобы из элементов были удалены все дополнительные данные и события.
Джон Ресиг утверждал, что знает способ быстро удалить все это и что
улучшит эти методы, но что-то воз и ныне там. Поэтому будем ждать улуч-
шенных функций уже в
Создание "на лету"
Прошлый пример, на самом деле, был нужен
для того, чтобы подобраться поближе к
интересному факту. Часто приходится
создавать какие-то вспомогательные дивы,
и, естественно, нас интересует самый
эргономичный способ это сделать. Казалось
бы, в чем проблема: кинул кусок html-кода,
и
$("<div></div>")
или
$("<div/>")
Второй вариант в 5 раз быстрее первого. Но это, естественно, не все: если нам надо создать не пустой див, а содержащий текст, из прошлых заметок станет ясно, что функция text тяжелая, и выгоды от нее не будет, и стоит создавать див "как есть".
$("<div>text</div>")
И не создавать, а потом добавлять текст:
$("<div/>").text("text")
Но это не каcается создания атрибутов, для них используются намно- го более "легкие" функции attr/css/addClass (http://mabp.kiev.ua/2009/03/29/jquery-profiling/), вот тут-то и имеет смысл вместо
$("<div style='background:red;'/>")
писать
$("<div/>").css({background:'red'});
— это даст небольшой, но выигрыш.
Множественные события
$(window).bind("resize load",null,function(){
$("#id").css({width:document.clientWidth})
});
Только при этом не забываем, что поведение IE8 не соответствует стандартам, и при загрузке страницы сначала происходит событие resize, а только потом load.
То же самое корректно и в обратную сторону:
$(window).unbind("resize load");
Но это не работает в версии 1.2.6, точнее, работает только с именованными функциями, а с анонимными не годится, их надо удалять по одной.
Одно событие на много элементов
Если случается повесить события на длинный список:
var ul = $("<ul/>");
for(var i=0,j=1000;i<j;i++)
$("<li>"+i+"</li>").click(function(e){
alert(this.innerHTML);
}).appendTo(ul);
ul.appendTo("body");
то в результате мы будем иметь 1000 одинаковых обработчиков событий. Вряд ли это добавит скорости нашей странице, поэтому можно воспользоваться маленькой хитростью и повесить всего один обработчик на родительский элемент:
var str = "";
for(var i=0,j=1000;i<j;i
str += ""<li>"+i+"</li>";
$("<ul/>")
.append(str)
.click(function(e){
alert(e.target.innerHTML);
})
.appendTo("body");
Нередко можно наблюдать ситуацию, когда производительность сайта в клиентском браузере недооценивается. Особенно часто это происходит в тех случаях, когда сайт собран "из коробки" и не специализирован под выполнения тех или иных конкретных задач. Ситуация может быть дополнительно осложнена низкоскоростным каналом связи у целевой аудитории пользователей.
В общем, все вышеописанное было справедливо для сайта PERSPEK-TIVA
Для анализа скорости загрузки в качестве стандарта стоит использо-
вать как webo.in, так и ETag или Last-Modified, а также, что
более существенно, сразу виден потенциальный выигрыш при минимизации файлов.
С помощью визуальной оптимизации (http://webo.in/my/action/load/) удобно посмотреть, как изменится диаграмма загрузки сайта, если применить все оптимизационные меры. И поскольку расчет производится аналитически, он обеспечивает достаточно большую точность (не нужно замерять по два-три раза, чтобы избежать случайных сетевых задержек).
Разобрав основные проблемные места сайта с помощью указанных инструментов, намечаем путь действий — и вперед. Дополнительно мож- но оценить время ответа с сервера (http://webo.in/my/action/timings/) для динамических файлов и понять, нужно ли что-то придумывать для оптимизации серверной части.
Базовые действия по оптимизации чрезвычайно просты: нам нужно объединить все текстовые файлы и применить для них gzip-сжатие. А также включить кэширование на достаточно длительный срок (это позволит
значительно ускорить по крайней мере открытие последующих страниц на этом сайте). Все это делается несколькими строками в конфигурационном файле Apache ( или .htaccess ):
AddOutputFilterByType DEFLATE text/html AddOutputFilterByType DEFLATE text/xml AddOutputFilterByType DEFLATE image/x-icon AddOutputFilterByType DEFLATE text/css AddOutputFilterByType DEFLATE application/x-javascript BrowserMatch ^Mozilla/4 gzip-only-text/html BrowserMatch ^Mozilla/4\.0[678] no-gzip BrowserMatch Konqueror no-gzip BrowserMatch \bMSIE !no-gzip !gzip-only-text/html Header append Vary User-Agent <FilesMatch .*\.(css|js|php|phtml|shtml|html|xml="")$> Header append Cache-Control private </FilesMatch> ExpiresActive On ExpiresDefault "access plus 1 month" <FilesMatch .*\.(shtml|html|phtml|php="")$> ExpiresActive Off </FilesMatch>
Данный фрагмент кода включает сжатие для тех типов файлов, для которых это разумно сделать, затем отключает сжатие для старых и неподдерживаемых браузеров. Заголовки
Объединение и минимизация файлов может быть достаточно проблематичным занятием, но для этой цели можно использовать пакетную оптимизацию (http://webo.in/my/action/packet/), загрузить один архив со всеми файлами, которые требовалось оптимизировать, — и на выходе получить уже готовые к применению версии.
Следующим шагом по соотношению "эффективность/сложность" будет работа с изображениями. Начать лучше всего с создания CSS
Попутно все GIF-изображения переводятся в PNG-формат. Для этого архив с изображениями прогоняется через всю пакетную оптимизацию. Отдельным пунктом для сайта www.vacLavak.ru стоит упомянуть работу с ани-мированными GIF. В исходной точке все банеры занимали порядка 180 Кб. При оптимизации повезло (удалось удалить ненужные кадры и немного уменьшить палитру), в итоге размер рекламного блока сократился вдвое.
В результате проведенных действий размер страницы уменьшился до 350 Кб, что было бы совсем неплохо, учитывая изначально "тяжелый" дизайн сайта.
Однако этого оказалось недостаточно.
Для более точного анализа реальной картины можно воспользоваться счетчиком времени загрузки (более подробно он описан в практическом приложении в книге "Разгони свой сайт"), который замеряет полное время загрузки у всех посетителей сайта для произвольной страницы. Установка его чрезвычайно проста: нужно получить код, установить его максимально близко к началу страницы в шаблоне (идеально — сразу после title) и пару дней собирать статистику (зависит от числа хитов; если на сайт заходит несколько десятков тысяч ежедневно, то хватит и дня или пары часов).
(рис 8.6) Скорость загрузки страниц на четвертом этапе клиентской оптимизации для www.vaclavak.ru, источник: webo.inПосле установки счетчика обнаружилась следующая картина: среднее время загрузки (уже оптимизированного) сайта составило 16 секунд, за 4 секунды страницы загружались у 58% пользователей.
Было решено существенно переработать логику загрузки страницы: вынести рекламные блоки в пост-загрузку, а загрузку самого JavaScript "отложить". Все это было сделано в лучших традициях "ненавязчивого"
JavaScript, которые описаны в седьмой главе книги "Разгони свой сайт". OnDOMReady (для точности измерения посещений), остальные — на window.onload.
В результате на странице сразу загружалось только основное содержание. Сразу после него —
(рис 8.7) Скорость загрузки страниц на пятом этапе клиентской оптимизации для www.vaclavak.ru, источник: webo.inЕдинственная сложность возникла с блоком валют — он вызывался через внешний JavaScript-файл, который в свою очередь содержал docu-ment.write. Его пришлось вставлять через динамический iframe, который получал данные, а потом отправлял родительской странице. Поскольку информер этот загружается достаточно медленно, получился значительный выигрыш во времени загрузки страницы
после его вынесения в постзагрузку.
После очередных замеров с помощью счетчика времени загрузки получилась следующая картина: среднее время загрузки — 7,3 секунды (ускорение более 100% от уже оптимизированного варианта), за 4 секунды сайт загрузился у 82% посетителей.
Благодаря существованию большого количества удобных инструментов для клиентской оптимизации (на http://webo.in/) удалось очень оперативно проработать клиентскую составляющую обычного в общем-то сайта. Только для широкополосного подключения скорость загрузки увеличилась в 7 раз (для коммутируемого доступа это число еще более значительно в связи с существенным уменьшением числа запросов, необходимых для первичного отображения страницы).
(рис 8.8) История применения клиентской оптимизации для www.vaclavak.ruСам процесс оптимизации относительно оценки самого webo.in мож- но представить следующим образом (в этом уже может помочь история проверок сайта, http://webo.in/my/action/history/):
Возможно, не все из описанных шагов необходимы для среднестати стического сайта, рассчитанного на рядовых пользователей Интернета, но сам процесс оптимизации (от простого к сложному, от более эффективного — к менее эффективному) должен, по-видимому, протекать именно таким образом.
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.