Главная | Web-дизайн | Логотипы | Баннеры | Flash | Простые сайты | Цены | Контакты | Статьи
Главная >> Статьи >> Создание больших web-проектов

Создание больших web-проектов

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

Большие скорости


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

Создание модулей


Смысл этого приема - вкомпилировать наиболее важные функции в сервер. Идея очень проста. Если мы посмотрим на соотношение времени, которое тратится на различные стадии выполнения запроса, то увидим интересную картину. Например, при выполнении простейшего perl-скрипта последовательно происходит следующее:

1) сервер Apache определяет perl-скрипт для запуска, подготавливает и запускает его;
2) запуск скрипта фактически начинается с запуска perl-интерпретатора (это файл, размером около полумегабайта). Perl-интерпретатор, запустившись, размещается на 2-х мегабайтах в памяти машины, и только после этого приступает к работе с пользовательским скриптом;
3) эта работа начинается с компиляции программы. Компиляция программы - это, как правило, один из самых длительных этапов обработки программы;
4) только после предварительной компиляции (в байткод) скрипт начнет выполняться.

Статистика удручает: время, которое тратится на запуск perl-интерпретатора и компиляцию скрипта, как правило, на порядок больше времени, за которое он выполняется.
На каждом сайте существуют узкие места - программы, которые вызываются очень часто. Например, баннерный движок. Как правило, на один просмотр страницы приходится два-три баннера, а значит и вызова программы. Понятно, что если избавиться от накладных расходов (пункты 2 и 3), работа сервера значительно ускорится. Это можно сделать двумя похожими способами.
Первый - написать модуль к Apache и вкомпилировать его в сервер. Именно так в баннерной сети Фламинго-2 (http://www.f2.ru), в создании которой я принимал участие, была реализована часть системы, которая раздавала баннеры пользователям. Это был модуль, написанный на языке C, который функционировал как часть сервера Apache и поэтому работал очень быстро.
Второй способ - использовать технологии предкомпиляции программ. Таких технологий достаточно много. Например, для perl-скриптов это могут быть FastCGI и mod_perl. Расскажу подробней о mod_perl. Это вкомпилированный (опять же в виде модуля) в Apache perl-компилятор. Во-первых, даже для простых скриптов (при надлежащей настройке) это исключает вторую стадию выполнения. Но кроме этого mod_perl дает возможность писать хэндлеры - обработчики определенных стадий выполнения запроса. Это очень мощная технология.

Использование конвейеров


Старайтесь не производить обработку данных в интерактивных скриптах. Записывайте их в лог-файлы, а затем агрегируйте и обрабатывайте уже отдельным процессом. Например, ответ пользователя в интерактивном голосовании может вызывать у вас изменения в десятке различных параметров статистики (распределение ответов, активность пользователей, общее число проголосовавших и так далее). Не проводите их сразу. Вместо этого разбейте процедуру на две части. Первая - непосредствен- но голосование, запись результата и вывод ответной страницы пользователю. Вторая - обработка голосования, изменение статистики и т.д.
Вообще надо стараться минимизировать количество интерактивных операций. В идеальном случае скрипт для учета голосования вообще ничего не делает, кроме записи информации в лог-файл. А для обработки данных из лог-файла можно запускать отдельный процесс-демон.
Для примера рассмотрим механизм обработки статистики в баннерной сети Фламинго-2. В ней был реализован 4-х ступенчатый конвейер:

1) Информация о каждом запросе записывалась в полный лог. Это была очень подробная информация и записывалась она без всякого сжатия, на которое потратилось бы много времени. Размер этого лога очень велик - одна запись в нем занимала 250 байт. Данные в этом логе не хранились дольше нескольких часов.
2) С периодичностью раз в 10 минут запускалась программа, которая обрабатывала полный лог и в компактном виде писала информацию в таблицы базы данных. На этой же стадии учитывались показы, изменялись временные таблицы, используемые для выдачи баннеров пользователю и для работы следующих стадий.
3) Часовой демон, который строил почасовую статистику, производил сложные географические расчеты и многое другое, запускался в конвейере один раз в час. Он уже не имел доступа к полному логу и использовал информацию исключительно из второй стадии.
4) В задачи последней стадии входила дневная ротация файлов, статистика, подведение балансов и рассылка почтовых предупреждений. Эта стадия работала каждые сутки поздно ночью, когда нагрузка на сервер была минимальной.

Как видите, механизм достаточно сложный, и наладить его корректную работу было нелегко. Чем больше стадий, тем больше проблем при их сопряжении друг с другом. Тем не менее, такая система позволяла достаточно эффективно распределять нагрузку и шустро работала на простом IDE-диске (расчетная пропускная способность была около 2-3 миллионов обращений в день при пиковой нагрузке 200 обращений в секунду). При этом система вела большое количество статистики.

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

Базы данных


Используйте хорошую базу данных. Какую выбрать? Единого рецепта нет. Все зависит от решаемой задачи. Если она достаточно простая и вам не требуется выполнять сложные SQL-запросы (например, вложенные), то наилучшим решением будет, пожалуй, база данных MySQL.
MySQL - один из самых простых серверов БД. Но даже в этой простой базе есть свои способы оптимизации для ускорения запросов. Например, не секрет, что INSERT - одна из самых длительных операций (вычисление физического адреса для вставки, вставка, решение проблемы фрагментации, изменение индексов и служебных таблиц). Хороший прием для ускорения работы скрипта, который вставляет данные в БД - замена операции INSERT операцией INSERT DELAYED (отложенная вставка). Обновление данных будет выполнено только тогда, когда это не приведет к замедлению работы сервера.
Другой пример: если внимательно почитать документацию MySQL, можно найти упоминание о таблицах, расположенных в памяти (HEAP tables). Очевидно, что операции с такими таблицами совершаются значительно быстрее. Heap-таблицы можно использовать для решения некоторых задач.
Существует большое количество параметров запуска сервера БД, оптимизирующих буферы сортировки, вычислений, количество детей и другие параметры. Как правило, вам заранее известно, что вы будете делать с базой, и для повышения быстродействия можно задать соответствующие параметры. Например, возьмем вполне реальную задачу: построение какого-нибудь каталога. Ясно, что это будет одна большая таблица с большим количеством индексов. Вы знаете, что будете использовать представления. Работа с этой таблицей будет заключаться в запросах по индексу без использования сортировки. Посмотрим, как можно настроить сервер БД на выполнение такой задачи (пример из MySQL 3.23.25):
  • join_buffer_size - буфер для создания представлений, по умолчанию равен 131072 байта;
  • key_buffer_size - буфер для работы с ключами и индексами. Размер по умолчанию - 1048540;
  • sort_buffer - буфер для сортировки. По умолчанию - 2097116 байт.

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

Резюмируем: при использовании базы данных работу скрипта можно значительно ускорить правильной настройкой сервера БД. В руководстве базы данных MySQL есть специальный раздел, посвященный оптимизации. За более подробной информацией можно обратиться на сайты:
Разработчики MySQL - http://www.mysql.com
Разработчики PostgreSQL - http://www.PostgreSQL.org/
Оптимизация MySQL - http://www.mysql.cz/information/presentations/presentation-oscon2000-20000719/index.html и http://support.ultrahost.ru/mysql_opt.php

Большие объемы


Еще одна проблема больших сайтов - большой объем информации. Если не применять никаких ухищрений, то поддержка простого html-сайта в какой-то момент потребует слишком много времени.

Объектно-ориентированное программирование


О пользе объектно-ориентированного подхода я уже рассказывал . Повторю вкратце. Каждый, кто хоть раз пробовал создавать динамические сайты, знает, что во многом это - очень однообразная задача. Гостевая книга, конференция, форма для отправления комментариев, подписка, регистрация. Как правило, эти скрипты слабо интегрированы и, в лучшем случае, используют общую библиотеку с константами и общими процедурами.

Однако если перечислить сущности, с которыми имеют дело вышеперечисленные скрипты, мы получим очень интересные результаты:

  • Сущность "пользователь". Имеет свое имя, фамилию, ник, пароль, электронный адрес: Используется практически во всех скриптах в разных ипостасях.
  • Сущность "сообщение". Вы можете возразить, что сообщения везде разные. Ничего подобного! Различаются формы представления сообщений, а данные, структура полей и методы обработки - одни. Автор, заголовок, тело - и так во всех проектах.

Вот фактически и все сущности, с которыми оперирует большинство скриптов на сайте. Гостевая книга (она, кстати, сама может быть объектом в более сложных проектах) представляет собой цепочку объектов класса "сообщение". Форум или конференция - те же сообщения, организованные иерархически. Отправка письма владельцу сайта - сообщение. Рассылка анонсов - перебор объектов класса "пользователь" и отправка каждому объекта класса "сообщение".
Было бы эффективно описать все эти объекты в одном месте, а потом строить из них, как из кирпичиков, программы и скрипты, просто вставляя вызовы объектов в код. К тому же, единое пространство сообщений, пользователей и других объектов значительно расширяет поле для творчества.
В этом и есть сущность объектного подхода. Вы создаете множество объектов - кирпичиков будущих программ - и из них строите свои сайты. Кроме того, вы можете использовать такие мощные методы ООП как наследование и полиформизм, без которых уже немыслимо построение крупных проектов.

Шаблонирование


Об этом я тоже расскажу вкратце; возможно этому будет посвящена статья в одном из следующих номеров "Программиста". Вернемся к системе Фламинго. Как был организован интерфейс этой баннерной сети? 400 видов статистики соответствуют 400 страницам? Нет. Один скрипт-шаблонизатор, которому передаются параметры - номер статистики и другие данные: даты, ограничения и т.д.
По уникальному номеру статистики скрипт считывал описание, которое состояло из имени файла с псевдо-html и имен файлов с SQL-запросами.


Общая схема очень проста - выполнить все SQL-запросы и вставить результаты в псевдо-html, получив таким образом полноценную страничку, и выдать ее пользователю. Например, для вывода статистики с номером 2 (информация об аккаунте), требовалось выполнить SQL-запрос data/queries/info.sql, результаты вставить в data/html/2.htx. Результат вывести на экран.
А вот как обстояло дело подробнее. Первая задача - формирование SQL-запроса. В него нужно вставить идентификатор пользователя и другие параметры, которые переданы скрипту. Типичный пример SQL-запроса (data/queries/info.sql):


При разборе такого запроса значение параметра вставлялось на место строки <--ИмяПараметра--> . Существовали и специальные параметры, например - <--UserName--> - имя пользователя и <--AccountId--> - вычисленный по имени идентификатор аккаунта.
Результат выполнения полученного запроса заносился в html следующим образом. Каждое полученное из базы данных значение получало "имя", с помощью которого обозначалось его местоположение в html-шаблоне. Имя было составным. Первая часть - порядковый номер SQL-запроса, вторая часть - индекс значения в массиве результатов.
Допустим, выполнялся SQL-запрос с порядковым номером 1 (для примера рассмотрим запрос data/queries/info.sql). Запрос возвращал массив значений. Соответственно, значение AccountName, возвращенное базой данных, имело порядковый номер 0 в этом массиве. В html-шаблоне место, куда необходимо было вставить AccountName обозначалось как <--1.1--> .

Несмотря на кажущуюся сложность схемы, она имеет ряд преимуществ. С ее помощью мы смогли за короткое время построить систему с более чем 400 видами различных статистик. Впоследствии для добавления новой статистики надо было только написать SQL-запросы, нарисовать HTML-шаблон и изменить конфигурацию скрипта-шаблонизатора. Новая страница статистики появлялась в системе автоматически.

Заключение


Я хотел бы еще раз повторить: нет решений на все случаи жизни. Каждый раз, в каждом проекте вам придется придумывать собственные методы оптимизации быстродействия и удобства работы. Я надеюсь, что приемы, о которых я рассказал, пригодятся вам.
Главная | Web-дизайн | Логотипы | Баннеры | Flash | Простые сайты | Цены | Контакты | Статьи
Copyright © Андрей Месник. Web-портфолио 2005-2007