Как облачный сервис Кабинет Дримкас справляется со спонтанными нагрузками

в 9:23, , рубрики: 54-ФЗ, RabbitMQ, Анализ и проектирование систем, Блог компании Дримкас, выравнивание ресурсов, высокая производительность, Дримкас, интернет-магазины, онлайн-кассы, очереди, Проектирование и рефакторинг, Развитие стартапа, спонтанные нагрузки, транспорт, трафик

image

Однажды днем у нас обрушился сайт. Сразу после ребута он падал снова. Мы знали, что это не DDOS, а органический трафик: к нам поступали типичные запросы, но сервера не справлялись. Увеличение мощности железа не помогало. Стало ясно, что пора оптимизировать нашу систему.

Молодым стартапам может быть интересно, как справляться с возросшими нагрузками на еще неокрепшее серверное ПО.

Кабинет Дримкас — облачный сервис для владельца кассы

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

Дримкас производит онлайн-кассы. В 2016 году приняли новую версию закона о контрольно-кассовой технике. Одно из главных нововведений в том, что каждая касса должна отправлять данные о продажах в налоговую в реальном времени. Для нас это значило, что теперь все кассовые аппараты должны быть подключены к интернету, чтобы передавать чеки в ФНС.

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

Как был устроен Кабинет вначале

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

Сложность в том, что у нас в рынке несколько моделей касс на Linux, одна на Windows 10 и фискальные регистраторы вообще без операционной системы. В планах были и другие устройства, но тогда еще никто не знал, на чем будут работать они. Это означало, что нужно поддерживать разные версии протоколов и форматов данных — разработка новых фич чрезмерно усложнялась.

Решили создать промежуточный узел — «Хаб», который будет поддерживать разные версии, модели касс, транспорты общения и даже кодировки, если понадобится. Сайт — «Кабинет Дримкас» — получит нормализованные данные и обобщенный протокол общения со всеми устройствами.

image
Хаб нормализует данные от разных моделей касс, а Кабинет общается по API

Чтобы проверить гипотезу — нужен ли вообще такой сервис пользователям? — запустили первую версию. Получили обратную связь — у нас просили отчеты, выгрузку данных в Эксель, работу с товарами, открытый API для интеграторов. Проект оказался востребован, мы выпускали новые фичи.

Хаб с Кабинетом общался стандартными HTTPS-запросами с таймаутом 5 секунд:

  • Кассы отправляли на Хаб информацию об открытии/закрытии смены и новых чеках. Раз в минуту они проверяли, есть ли для них новые задания.
  • Хаб получал задания от касс, хранил их у себя и раз в 10 секунд пачкой по 100 заданий засылал в Кабинет. Получал задания от Кабинета и отправлял их кассе, когда она в очередной раз к нему постучится.
  • Кабинет принимал и обрабатывал чеки, смены и отправлял на кассы изменения старых товаров и создание новых.

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

Чеки — самые тяжелые из всех видов заданий. Нужно пробежаться по всем позициям, создать новые товары в Postgres, а затем положить весь чек в MongoDB. Почему чеки решили хранить в монге — отдельный вопрос, есть свои плюсы и минусы. Особенно это актуально сейчас, когда данных стало очень много и нужно делать сложные выборки с агрегациями.

Так система работала, пока к Кабинету было подключено около двух тысяч касс, а доля чеков среди остальных заданий была не больше 15%.

Когда пользователей стало больше, мы перестали справляться с нагрузками

Интересно, как на нас отражался нестабильный интернет наших пользователей. Когда в магазинах пропадает интернет, касса продолжает продавать, собирая чеки. Как только интернет появляется — отправляет все данные сразу и в ОФД, и в Хаб. Нагрузка на наши сервера подскакивала. С ростом количества касс подобный график нагрузки стал настоящей угрозой.

Мы стали замечать подлагивания днем, которые со временем усиливались. Потом сервер просто упал и не мог подняться, потому что его сразу заваливало новыми заданиями.

У Хаба не было проблем — он складывал все данные в БД без тяжелой логики и отправлял дальше. Вся беда заключалась в том, что пачка задач с большой долей чеков могут обрабатываться дольше таймаута запроса на nginx. Это приводило к 500 timeout от Кабинета, и Хаб сразу же пытался скормить те же данные повторно, хотя Кабинет всё ещё обрабатывал предыдущие. В итоге Хаб просто начинал DOS Кабинета, пока последний не упадет.

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

Чтобы не возникало давки, мы организовали очередь

Если смотреть на усредненную статистику — мощности железа хватало. Самих данных было немного, и в течение дня за пиковыми нагрузками следовал спад. Нужно было сгладить нагрузку, чтобы не падать во время пиков.

Решение — очередь заданий, куда можно складывать задания с какими угодно интервалами и скачками, чтобы обрабатывать их по мере возможности. Так как проблема была на стороне Кабинета, решили это прикрутить внутри него. В качестве очереди выбрали rabbitMQ. Все задания от Хаба по одному попадали туда и воркеры их обрабатывали.

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

Если раньше при сохранении товара на сайте Хаб лежал, то мы выкидывали ошибку «Повторите позже». Сейчас данные сохранятся в БД. Задача кладется в очередь намного быстрее, потому что мы не дожидаемся подтверждения от Хаба, и он сам ее забирает по мере возможности. Если вдруг Кабинет не закончил обработку задачи, а связь с воркером прервалась, то эта задача снова появится в очереди. Всё это Кролик делает из коробки, никаких обработок ошибок на нашей стороне не требуется.

Увидев, как это круто работает, решили узнать верхнюю границу прочности транспорта и провели нагрузочное тестирование. Офисная сеть сопротивлялась, но все-таки упала. А Кролик все задачи просто накопил у себя и ждал, пока воркеры их обработают. Отсюда вывод — возможностей кролика нам хватает с избытком, главное дать ему немного SSD и побольше RAM.

Вывод

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

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

Автор: ilnuribat

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js