Современные информационные технологии/2. Вычислительная техника и программирование

 

Осип А. И., Кажикенова С. Ш.

Карагандинский государственный университет имени Е. А. Букетова, Казахстан

Разработка интернет портала, рассчитанного на высокие нагрузки, с применением методов серверной оптимизации

 

При разработке web-продуктов программисты редко задумываются над тем, что на одну кнопку в один момент времени могут нажать одновременно более 2000 человек. Поэтому перед запуском программного продукта необходимо проводить нагрузочное тестирование и определять максимально допустимую посещаемость.

Нагрузка - процент использования ресурсов сервера в момент выполнения задачи. У каждого сервера есть ограниченные ресурсы: процессор, память, дисковая система. В момент выполнения задачи выделяется некоторая часть свободных в настоящий момент ресурсов. В зависимости от типа производимых действий той или иной задаче может требоваться больше или меньше ресурсов: для выполнения одних задач достаточно 1 % процессорного времени, потому что основное время тратится, например, на ввод/вывод или на ожидание данных, некоторые же сложные вычисления и обработка данных могут потребовать 70 % ресурсов процессора или более. Точно так же различным задачам требуется больше или меньше оперативной памяти, высокая или низкая дисковая активность.

Чаще всего нагрузка возникает из-за того, что для генерации одной страницы сайта требуется проведение большого цикла обработки данных. Обычно это встречается в CMS, у которых сложная структура страниц/категорий/пользователей/шаблонов, и для генерации каждой страницы требуется выборка большого количества данных из базы, последующая обработка этих данных и выведение их по шаблону, парсинг которого тоже требует вычислительных ресурсов.

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

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

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

Плохая организация сайта может стать причиной лишней нагрузки на него. Например, одну и ту же страницу можно получить несколькими разными способами (/index.php?cat=1&id=2 или /index.php?id=02&cat=01), и на сайте используются все эти способы. Естественно, поисковые роботы считают такие ссылки разными документами и пытаются получить одну и ту же страницу несколько раз по разным ссылкам.

В конце концов, у сайта может быть просто большая посещаемость, вызывающая высокую нагрузку.

Для снижения нагрузки на сайты применяют методы серверной оптимизации, в том числе кэширование кода и данных, оптимизацию запросов к базе данных, неблокирующий ввод/вывод [1 - 4].

Кэширование

При обработке сценария на PHP обычным интерпретатором выполняются следующие действия:

·        чтение файла,

·        генерация байткода,

·        выполнение кода,

·        выдача результатов.

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

Для обхода этого узкого места разработаны акселераторы PHP - модули, кэширующие скомпилированный байт-код в памяти и/или на диске и в несколько раз увеличивающие производительность PHP.

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

Большинство библиотек кэширования позволяет кэшировать значения переменных. Очень полезно сохранять значения конфигурации или данные, которые сложно вычислить (получить) и которые не меняются (возможно не меняются в течение некоторого времени, тогда на базе такого кэширования можно реализовать кэширование с устареванием).

В настоящее время получили распространение четыре системы кэширования:

             eAccelerator - открытый и свободный проект, выполняющий роли акселератора, оптимизатора и распаковщика;

             APC - открытый и свободный оптимизатор кэша для PHP. Позволяет хранить массивы как наборы констант;

             XCache - быстрый и стабильный акселератор PHP-кода. Позволяет выполнять инкремент и декремент числового значения в кэше, а также проверять наличие ключа в кэше без пересылки значения;

             Memcache - система объектного кэширования распределенной памяти, которая реализует сервис кэширования данных в оперативной памяти. Позволяет кэшировать данные в оперативной памяти одного или нескольких из множества доступных серверов.

Сходство данных систем заключается в том, что они позволяют хранить пару ключ-значение в оперативной памяти, являются дополнением к PHP и обладают возможностью кэширования промежуточного кода, за исключением memcache, который выполняется в виде отдельного приложения и может выполнятся на отдельном сервере [3 - 11].

Оптимизация запросов к базе данных

При традиционном способе обработки запросов на каждый запрос к анонсеру трекера выполнялось бы как минимум 3 отдельных INSERT или UPDATE, клиент ждал бы их исполнения, таким образом серверу базы данных пришлось бы выполнять по 3 запроса на каждое обращение к анонсеру.

Результат каждого обращения к анонсеру записывается в несколько таблиц. У пользователя увеличивается скачанный и залитый трафик. Обновляется статистика у торрента. Заполняется таблица текущих участников раздачи.

При оптимизации анонсер выполняет запросы не сразу, а накапливает их в пачку INSERT… VALUES (...), (...). ..., (...) ON DUPLICATE KEY UPDATE f1=VALUES(f1), ..., fN=VALUES(fN), и исполняет раз в несколько секунд, за счёт чего число запросов в базу уменьшается с нескольких на запрос к анонсеру до нескольких в минуту [2].

Неблокирующий ввод/вывод

В 90-е годы при работе с сокетами использовался блокирующий ввод/вывод, когда при вызове методов recv и send текущий поток зависал до ожидания результатов. Для каждого принятого соединения создавался отдельный процесс (fork), в котором шла обработка его запроса. Каждый процесс требовал память под стэк и процессорное время на переключение контекста между процессами. При высокой нагрузке этот метод может быть не слишком эффективным, и необходимо использовать другие паттерны обработки соединений.

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

Один из методов реализации неблокирующего ввода/вывода - создание пула потоков (thread pool), которым передаётся запрос для обработки, после чего поток возвращается обратно в пул. Если свободных потоков нет, запрос ждёт в очереди. Такой подход позволяет уменьшить общее число используемых потоков, и каждый раз не приходится создавать новый и убивать его после завершения обработки запроса.

Epoll - новый системный вызов, появившийся в Linux 2.6 и призванный заменить устаревший select (а также poll). В отличие от старых системных вызовов, сложность которых O(n), epoll использует алгоритм O(1), что означает хорошее масштабирование при увеличении количества прослушиваемых дескрипторов. Select реализует линейный поиск по списку прослушиваемых дескрипторов, что приводит к сложности O(n), в то время как epoll использует обратные вызовы структуры файла в ядре [2, 12].

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

Разработана структура база данных.

Хранение пользовательских сессий вынесено в memcache, что дает возможность горизонтального масштабирования.

Для снижения нагрузки результаты самых популярных и не критичных к актуальности запросов к MySQL кэшируются в APC, который не использует TCP соединение и благодаря этому работает в несколько быстрее, чем memcache.

Примененные методы серверной оптимизации позволили создать интернет портал, выдерживающий высокие нагрузки.

 

Литература:

1.          http://www.elipseart.ru/faq/408/

2.          http://habrahabr.ru/blogs/server_side_optimization/53360/

3.          http://habrahabr.ru/blogs/server_side_optimization/70167/

4.          http://habrahabr.ru/blogs/server_side_optimization/52475/

5.          http://ru.wikipedia.org/wiki/Alternative_PHP_Cache

6.          http://bart.eaccelerator.net/doc/phpdoc/

7.          http://www.php.net/apc

8.          http://xcache.lighttpd.net/wiki/XcacheApi

9.          http://www.php.net/memcache

10.      http://habrahabr.ru/blogs/php/31446/

11.      http://club.shelek.ru/viewart.php?id=300

12.      http://www.opennet.ru/base/dev/epoll_example.txt.html