Однажды днем у нас обрушился сайт. Сразу после ребута он падал снова. Мы знали, что это не DDOS, а органический трафик: к нам поступали типичные запросы, но сервера не справлялись. Увеличение мощности железа не помогало. Стало ясно, что пора оптимизировать нашу систему. Молодым стартапам может быть интересно, как справляться с возросшими нагрузками на еще неокрепшее серверное ПО. Дримкас производит онлайн-кассы. В 2016 году приняли новую версию закона о контрольно-кассовой технике. Одно из главных нововведений в том, что каждая касса должна отправлять данные о продажах в налоговую в реальном времени. Для нас это значило, что теперь все кассовые аппараты должны быть подключены к интернету, чтобы передавать чеки в ФНС.  Раз наши кассы все равно шлют данные о продажах, кажется логичным собрать эти данные в облаке, чтобы владелец всегда имел к ним удаленный доступ. Так и появился Кабинет Дримкас. Приступили к реализации. Первую идею — создать один логический сервер, к которому напрямую будут обращаться все кассы — разработчики отвергли.  Сложность в том, что у нас в рынке несколько моделей касс на Linux, одна на Windows 10 и фискальные регистраторы вообще без операционной системы. В планах были и другие устройства, но тогда еще никто не знал, на чем будут работать они. Это означало, что нужно поддерживать разные версии протоколов и форматов данных — разработка новых фич чрезмерно усложнялась. Решили создать промежуточный узел — «Хаб», который будет поддерживать разные версии, модели касс, транспорты общения и даже кодировки, если понадобится. Сайт — «Кабинет Дримкас» — получит нормализованные данные и обобщенный протокол общения со всеми устройствами. Чтобы проверить гипотезу — нужен ли вообще такой сервис пользователям? — запустили первую версию. Получили обратную связь — у нас просили отчеты, выгрузку данных в Эксель, работу с товарами, открытый API для интеграторов. Проект оказался востребован, мы выпускали новые фичи. Если Кабинет по какой-либо причине не смог принять хотя бы один чек из пачки, Хаб предполагал, что не принята ни одна задача, и засылал всю пачку целиком ещё раз, пока Кабинет не отчитается об успешном принятии всей пачки. Чеки — самые тяжелые из всех видов заданий. Нужно пробежаться по всем позициям, создать новые товары в Postgres, а затем положить весь чек в MongoDB. Почему чеки решили хранить в монге — отдельный вопрос, есть свои плюсы и минусы. Особенно это актуально сейчас, когда данных стало очень много и нужно делать сложные выборки с агрегациями. Так система работала, пока к Кабинету было подключено около двух тысяч касс, а доля чеков среди остальных заданий была не больше 15%.  Интересно, как на нас отражался нестабильный интернет наших пользователей. Когда в магазинах пропадает интернет, касса продолжает продавать, собирая чеки. Как только интернет появляется — отправляет все данные сразу и в ОФД, и в Хаб. Нагрузка на наши сервера подскакивала. С ростом количества касс подобный график нагрузки стал настоящей угрозой.  Мы стали замечать подлагивания днем, которые со временем усиливались. Потом сервер просто упал и не мог подняться, потому что его сразу заваливало новыми заданиями. У Хаба не было проблем — он складывал все данные в БД без тяжелой логики и отправлял дальше. Вся беда заключалась в том, что пачка задач с большой долей чеков могут обрабатываться дольше таймаута запроса на nginx. Это приводило к 500 timeout от Кабинета, и Хаб сразу же пытался скормить те же данные повторно, хотя Кабинет всё ещё обрабатывал предыдущие. В итоге Хаб просто начинал DOS Кабинета, пока последний не упадет. Увеличим таймаут на nginx или уменьшим количество отправляемых за раз заданий — получим небольшую отсрочку и через месяц вернемся к той же проблеме, но масштаб последствий вырастет. Подключенных касс станет больше, мы подведем больше людей. Если смотреть на усредненную статистику — мощности железа хватало. Самих данных было немного, и в течение дня за пиковыми нагрузками следовал спад. Нужно было сгладить нагрузку, чтобы не падать во время пиков.  Решение — очередь заданий, куда можно складывать задания с какими угодно интервалами и скачками, чтобы обрабатывать их по мере возможности. Так как проблема была на стороне Кабинета, решили это прикрутить внутри него. В качестве очереди выбрали rabbitMQ. Все задания от Хаба по одному попадали туда и воркеры их обрабатывали. Идея оказалось настолько удачной, что Хаб захотел сделать то же самое у себя, ведь мы тоже иногда могли выслать разом сто тысяч заданий. Так мы решили использовать rabbitMQ в качестве транспорта между Хабом и Кабинетом.  Если раньше при сохранении товара на сайте Хаб лежал, то мы выкидывали ошибку «Повторите позже». Сейчас данные сохранятся в БД. Задача кладется в очередь намного быстрее, потому что мы не дожидаемся подтверждения от Хаба, и он сам ее забирает по мере возможности. Если вдруг Кабинет не закончил обработку задачи, а связь с воркером прервалась, то эта задача снова появится в очереди. Всё это Кролик делает из коробки, никаких обработок ошибок на нашей стороне не требуется.  Увидев, как это круто работает, решили узнать верхнюю границу прочности транспорта и провели нагрузочное тестирование. Офисная сеть сопротивлялась, но все-таки упала. А Кролик все задачи просто накопил у себя и ждал, пока воркеры их обработают. Отсюда вывод — возможностей кролика нам хватает с избытком, главное дать ему немного SSD и побольше RAM.  Нельзя считать, что первоначальное общение через пачки заданий было неверным. На ранних этапах разработки основной ресурс — время. Если бы мы задумывались о слишком далекой перспективе и оптимизировали все наперед, то до релиза могло бы и не дойти. Таким образом, главное — вовремя понять, что ваш сервис перерос старые решения, и для стабильной работы и последующего роста нужно притормозить развитие новых фич и пересмотреть архитектуру проекта. ex-CEO huntersales.ru. Node.js, Mongo, Devops