Как мы увеличили скорость обработки сервисных сообщений в 10 раз и еще 3 кейса про масштабирование проектов

Сформировать 400 000 документов за рабочий день, одновременно загрузить информацию о тысячах доменов, в 10 раз увеличить скорость обработки данных сайта, обеспечить стабильность интернет-магазина при росте посещаемости — решать такие задачи помогает горизонтальное и вертикальное масштабирование. Разбираем на примере наших проектов и кейса клиента, как повысить производительность веб-проекта. 

24d373eec7a5746c6cb8b2045dccbab9.png

Кейс 1: ускорили время формирования документов с 24 до 8 часов

Для подготовки клиентских документов мы используем внутренний самописный сервис. У нас есть биллинговая система, которая фиксирует все транзакции. Сервис берет необходимые данные и на их основе генерирует готовый файл. Так раз в месяц мы формируем отчетность для пользователей, это более 400 000 документов на основании более 5 млн транзакций. 

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

Решение. Пошли путем вертикального масштабирования и повысили вычислительные возможности сервера. Увеличили ядра процессора с 4 до 32 и добавили оперативки с 8 до 24 ГБ. Архитектуру приложения не меняли.

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

Во время тестирования были сложности: нарушение прав доступа, несовместимость библиотек, необходимость репликации базы данных и тому подобное. Но это стандартные вещи, тем более что наш сервис для подготовки документов является частью внутреннего многолетнего проекта компании и содержит легаси-код. Чтобы увеличить производительность системы, мы перешли с PHP 5.6 на PHP 7.4, который работает в 1,5–2 раза быстрее. Из-за этого некоторые библиотеки оказались несовместимы.

После того как устранили ошибки, система стала формировать на 15% больше документов и делать это в три раза быстрее — за 8 часов. Предел производительности теперь составляет 1,5 млн документов. 

Кейс 2: повысили скорость загрузки данных при обращении к сторонним сервисам

Мы разработали приложение, которое показывает информацию о доменах: принимает запрос пользователя, отправляет его к Whois-сервису, а затем выдает ответ. Раньше две эти операции происходили последовательно, поэтому ответ мог занимать от 1 до 5 секунд. Например, на загрузку информации о 20 доменах требовалось от 20 до 100 секунд. 

Но иногда возникали дополнительные задержки более 30 секунд. Так как Whois-сервисы имеют лимиты на количество запросов в единицу времени с одного IP-адреса, например 20 операций в секунду, наш самописный сервис иногда не справлялся с нагрузками. При этом за нарушение лимитов IP-адрес могли просто заблокировать.

Решение. Пошли по пути горизонтального масштабирования и добавили прокси-сервер с пулом IP-адресов. 

В основе прокси-сервера лежит самописный демон на Perl. Он назначает IP-адреса на запросы пользователей и направляет их к конкретному Whois-сервису в соответствии с таблицей динамической маршрутизации. 

Так выглядел код, когда запрос шел напрямую к Whois:

public function getAnswer(): string 
{
        $output = '';
        $conn = fsockopen('whois.nic.ru',43 , $errno, $errstr, 30);
        stream_set_timeout($conn, 43);
        fputs($conn,  $domain. "\r\n");
        $time = time();
        while (!feof($conn)) {
            $output .= fgets($conn, 128);
            if (time() >= $time + 30) {
                fclose($conn);
                throw new SocketException('Whois Error: Connect timeout');
            }
        }
        fclose($conn);
        return $output;
}

Вместо whois.nic.ru стали обращаться к прокси-серверу. Выбрав такое решение, мы внесли минимальные изменения в проект. 

В результате масштабирования через пул IP-адресов удалось распараллелить обращения к Whois-сервисам и повысить производительность системы по latency.

Теперь пользователь может получать информацию одновременно о нескольких доменах 

Теперь пользователь может получать информацию одновременно о нескольких доменах 

Но у такого решения есть ограничения. Если потребуется собирать данные, например, о 100 000 доменов, IP-адресов не хватит. И даже мощный сервер с большим объемом памяти не решит проблему. В этом случае нужно пойти другим путем — например, снизить скорость запросов, увеличив холд между ними. 

Кейс 3: увеличили производительность внутреннего сервиса в 10 раз

Среди самописных приложений у нас также есть сервис мониторинга, который обрабатывает результатов проверок доступности сайтов. Чтобы обеспечить бесперебойность в бизнес-процессах пользователей, необходимо было сделать сервис отказоустойчивым и высокопроизводительным. 

Решение. Здесь так же, как и в прошлом кейсе, применили горизонтальное масштабирование:

  • Повысили отказоустойчивость. Сервис мониторинга использует множество различных нод, расположенных в том числе не на нашем хостинге, — так он проверяет доступность сервисов из разных концов страны. Чтобы обеспечить доступность данных на каждой ноде, мы настроили репликацию базы данных MongoDB. Выбрали эту БД, поскольку у нее высокая надежность, она совместима с проектами на Python и имеет штатный механизм репликации.

    Также мы дублировали все микросервисы, которые есть в сервисе мониторинга: микросервис репликации MongoDB, проверки, отправки результатов проверки в брокер сообщений, микросервис, который читает сообщения, и другие.

  • Повысили скорость обработки данных. Для этого использовали шардинг: разделили один сервис, который шлет сообщения в брокер сообщений, на десять сервисов. Каждый получившийся сервис начал работать только с одной очередью сообщений. Бенчмарк-тесты показали, что скорость взаимодействия выросла: раньше было 1500 сообщений в секунду, стало — 15 000.

Кейс 4: распределили нагрузку на сервер с помощью балансировщика 

Это кейс нашего клиента, который держит на хостинге интернет-магазин. Он масштабировал проект, когда выросло количество посетителей: потребовался отказоустойчивый сервер, чтобы сайт внезапно не упал. 

Решение. Клиент применил горизонтальное масштабирование с помощью нашего балансировщика нагрузки Load Balancer. Балансировщик представляет собой два кластера серверов в разных дата-центрах с резервированием и автоматическим failover. Работает по двум алгоритмам балансировки: Round Robin / Weighted Round Robin и Least Connections. Умеет проверять доступность, запоминать сессии, поддерживает proxy protocol и backend keep-alive. 

Клиент скопировал свой виртуальный сервер, настроил репликации баз данных между исходниками и копией и синхронизировал их. Так получилось два идентичных сервера. 

Затем в локации, где расположен первый сервер, подключил балансировщик с алгоритмом работы least connections и правилом балансировки двух серверов. После того как балансировщик был создан, настроил в DNS его домен. Так получилась отказоустойчивая система интернет-магазина.

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

Краткие итоги

Мы стали задумываться о масштабировании уже на этапе разработки. Если это возможно, сразу закладываем запас по производительности и обеспечиваем отказоустойчивость. Так потом не приходится вносить глобальные изменения в архитектуру. Если масштабировать нужно готовый проект, стараемся делать это с минимальными вмешательствами — например, как было с сервисом, который формирует отчетные документы.

© Habrahabr.ru