В августе 2015 года мы запустили новый рекламный проект — Атуко .
Это система управления мобильной рекламой, ориентированная на профессионалов.
В Atuko мы фокусируемся на управлении одним каналом трафика — моя цель , основная рекламная система Mail.ru, объединяющая рекламу в Одноклассниках, мобильном ВК и некоторых других ресурсах Mail.ru и охватывающая > 90% аудитории Рунета.
И конечно, рекламодателям нужны инструменты для создания кампаний, анализа результатов и управления ими.
Нам хотелось бы рассказать вам, как именно мы подошли к созданию этих инструментов и архитектуры системы.
Это не первый проект нашей команды в сфере рекламных технологий.
Мы занимаемся разработкой систем управления рекламой с 2009 года, создаем инструменты для Яндекс.
Директ, Google Adwords, Google Analytics, ВК, [email protected] и других каналов.
Мы даже поймали Бегун и времена, когда это было актуально :)
За это время мы столкнулись со многими подводными камнями и неожиданностями, связанными с особенностями работы рекламных площадок, их API, а также с необычными задачами самих рекламодателей – и успели накопить большой опыт! Обо всем рассказать в одной статье не получится, поэтому если будет интерес, напишем серию статей, в которых я постараюсь поделиться полученными знаниями.
В этой статье я хочу рассказать вам ключевые вещи об архитектуре и инфраструктуре Атуко — и почему мы сделали так, а не иначе.
Прошлый опыт и важные уроки
Из нашего прошлого опыта мы извлекли, среди прочего, следующие важные уроки:- Требуется гибкое масштабирование во всех узлах системы.
Невозможно заранее предсказать, на какую часть системы вырастет нагрузка: анализ, создание, просмотр и т. д. Позвольте мне привести вам небольшой пример.
При управлении контекстной рекламой (Яндекс.
Директ и Google AdWords) поначалу все шло хорошо, рост был плавным.
В какой-то момент появляется действительно крупный клиент — и для реализации его задач нужно управлять 9 миллионами ключевых слов — а это во много раз больше, чем остальные клиенты вместе взятые.
Некоторые части системы (например, получение конверсий из Google Analytics) легко справились с увеличением нагрузки, а вот другие (например, получение статистики по всем ключевым словам) потребовали сильной оптимизации.
И самое неприятное, что объем этого клиента влиял на работу всей системы в целом — и, соответственно, других клиентов.
Это научило нас изоляции и гибкости отдельных частей системы, а также умению изолировать клиентов друг от друга.
- Необходимо настроить функционал под конкретного клиента.
Зачастую у отдельных клиентов есть свои уникальные требования, и объединить задачи разных клиентов в одном универсальном решении может быть сложно или невозможно – и более эффективное решение – настроить функционал под конкретного клиента.
Однако, конечно, эта настройка не должна влиять на других пользователей.
Таким образом, нам нужна возможность запускать модифицированный функционал для отдельных клиентов, и при этом не создавать для каждого клиента отдельную копию всей системы.
- Минимизация зависимости от фреймворков и языков программирования.
Например, один из созданных нами проектов существует дольше, чем фреймворк, на котором он построен — поддержка и развитие фреймворка прекратились.
Кроме того, привязка всего проекта к одному языку программирования снижает эффективность — нет возможности использовать оптимальный язык для каждой задачи, нет возможности использовать новые языки программирования.
Общую схему проекта можно изобразить следующим образом:
Микросервисы
Прежде всего, мы решили построить всё на микросервисах.Каждый микросервис предоставляет HTTP API и может быть реализован в любом стеке технологий.
Это дает нам возможность незаметно для других масштабировать каждый сервис, поскольку HTTP API может скрывать как одну копию сервиса, так и целый кластер с собственным балансировщиком.
Кроме того, каждый микросервис настолько прост, что в нем можно разобраться, не погружаясь полностью во весь проект, что упрощает разработку, а в крайнем случае, переписывание сервиса с нуля за достаточно короткий промежуток времени.
Таким образом мы также получаем независимость от используемых технологий, и даже если фреймворк в микросервисе устарел, мы можем перенести микросервис на новую технологию.
И конечно, мы можем использовать другой язык программирования, если понимаем, что на нем эта задача решается лучше.
Один из частых вопросов, возникающих при использовании микросервисов, — как разделить функциональность на микросервисы.
Для себя мы решили, что выделяем некий функциональный блок в отдельные микросервисы, которые делить на более мелкие части не имеет смысла.
Приведу пример: в Атуко можно создать большое количество рекламных кампаний и объявлений на рекламной платформе, загрузив специально сгенерированный файл Excel. Помимо интерфейса в этом процессе участвуют три микросервиса:
- анализ файла Excel и создание набора данных для загрузки в myTarget
- проверка набора данных на соответствие правилам рекламной площадки
- отправка данных в myTarget
При этом два последних сервиса — проверка корректности данных и отправка данных на рекламную платформу — используются и в других сценариях работы с Атуко (например, рекламные объявления можно создавать не только через Excel, но и через браузер).
Сервисам все равно, откуда они берут данные — они просто делают свою работу и передают результат дальше.
Но с разделением на микросервисы возникают новые проблемы.
В частности, отладка становится более сложной, поскольку в одном действии задействовано несколько служб, что затрудняет поиск виновника.
Тестирование тоже несколько меняется и значимость интеграционного тестирования сильно возрастает, ведь «в вакууме» сервисы могут работать, но вместе они терпят неудачу.
В ходе разработки также возникают дополнительные требования.
В частности, необходимо добиться того, чтобы результат службы действительно пошел дальше.
И, конечно, очень важен хорошо продуманный мониторинг.
Однако любой из этих пунктов – тема как минимум отдельной статьи, а то и целой книги.
Диспетчер
Понимая, что количество микросервисов будет очень быстро расти, мы увидели потенциальную проблему связи между сервисами.Если сервисы общаются друг с другом напрямую, то будет сложно получить полную картину их взаимодействия, а это приведет к проблемам с будущими модификациями сервисов.
Чтобы уменьшить их связность, мы решили ввести центральный микросервис, отвечающий за связь между остальными, — диспетчер.
По результатам своей работы каждый микросервис отправляет событие диспетчеру с результатами, а тот, в свою очередь, отправляет событие подписанным сервисам.
Таким образом, каждому микросервису не нужны знания о внешней среде.
Вместо этого ему просто нужно подписаться на определенные события в диспетчере и по результатам своей работы отправлять события в этот же диспетчер.
Единицы
Внедрение диспетчера решает и еще одну нашу задачу — создание индивидуального функционала под конкретного клиента.Это возможно за счет реализации модулей — каждый блок представляет собой комбинацию диспетчера и работающих с ним микросервисов.
Рассмотрим случай, когда одному из клиентов необходимо загрузить конверсии из собственной CRM-системы в уникальном формате, требующем уникальной обработки.
Эту проблему можно решить, если разных пользователей «обслуживать» разные микросервисы.
Запускаем 2 блока: у каждого из них свой диспетчер и свой набор сервисов.
При этом в одном из блоков сервисы работают по обычной схеме, а в другом они заменены сервисами, обрабатывающими конверсии из CRM клиента.
При этом некоторые вещи никогда не будут дублироваться в разных подразделениях — например, взаимодействие с внешними системами.
В случае с Atuko, например, существуют ограничения на использование myTarget API — и поэтому внешняя связь происходит через один микросервис, контролирующий частоту запросов.
Кстати, внутри блока мы можем менять не только работу отдельных сервисов, но и количество сервисов — например, добавляя новые этапы обработки данных или процедуры проверки.
Запуская дополнительные блоки, мы также упрощаем масштабирование по серверам, а также значительно упрощается обслуживание — вместо нескольких копий системы дублируются только отдельные элементы.
Конечно, этот подход тоже имеет свои нюансы.
Например, диспетчер будет очень загруженной службой, обрабатывающей всю связь между узлами системы.
Также следует сразу учитывать, что не все события имеют одинаковую важность, и разные события должны обрабатываться с разными приоритетами — иначе многие незначительные события могут замедлить обработку других, требующих немедленного реагирования.
Например, если пользователь отправил команду на остановку кампании, то эту задачу необходимо выполнить немедленно, а вот фоновое обновление статистики может подождать.
Докер
В целом описанный выше подход позволил нам подготовиться к возможным трудностям.
Но возникла другая задача, связанная не столько с процессом разработки, сколько с процессом эксплуатации.
Как управлять всем этим бизнесом? Каждый микросервис может быть реализован с использованием любой технологии, на любом фреймворке и иметь свои зависимости от различных библиотек.
Например, на данный момент у нас есть микросервисы на golang, python и php. И для решения этой проблемы мы используем Докер .
На основе каждого микросервиса создается Docker-образ (образ), на основе которого можно запустить неограниченное количество сервисов.
При этом они могут располагаться на разных машинах, что также упрощает масштабирование.
Обратный прокси
Все звонки проходят через обратный прокси.Это позволяет при поднятии другого контейнера с сервисом просто добавить нужную запись в нужный апстрим, а обратный прокси сам будет распределять трафик.
В настоящее время мы используем nginx — Но мы продолжаем рассматривать другие варианты.
Кроме того, для развертывания мы используем методику Сине-зеленое развертывание — это значит, что сервисы как с новым, так и со старым функционалом могут работать одновременно.
И в этом случае снова выручает обратный прокси, предоставляющий возможность распределить трафик в необходимых пропорциях между двумя версиями, и окончательно перейти на новую только убедившись в ее полной работоспособности.
DNS
Когда мы приступили к разработке, у Docker не было достаточно развитых сетевых возможностей.При этом мы хотели удобный доступ к микросервисам, недоступность бэкенда извне и размещение каждого блока в своей подсети.
Поэтому мы решили этот вопрос самостоятельно, подняв свой внутренний DNS. Теперь доступ к конкретному сервису происходит просто по его названию и названию объекта.
На данный момент мы продолжаем использовать DNS, но при этом рассматриваем и другие варианты, которых сейчас появляется все больше.
Кстати, это еще одно преимущество микросервисной архитектуры — довольно легко внедрять новые инструменты, способные облегчить нам жизнь, ускорить выпуск нового функционала или повысить надежность.
Это также гарантирует, что у нас не возникнет проблема устаревшего кода — устаревшие сервисы можно довольно легко заменить их текущими версиями.
Заключение
Хотелось бы поговорить о результатах применения этого подхода.Спустя год работы с похожей архитектурой мы по-прежнему довольны выбранным нами микросервисным подходом.
Несмотря на возникшие трудности и новые проблемы, которых раньше не было, подход в целом себя оправдал и решает поставленные задачи.
И, конечно же, Docker делает нашу жизнь намного проще — для нас это отличный инструмент сборки и доставки.
Я могу с уверенностью рекомендовать его всем.
А объединение подхода на основе микросервисов с Docker дает ряд преимуществ при разработке, тестировании и тем более в эксплуатации.
Наблюдая сейчас за стремительным ростом количества статей, отчетов и видео на тему микросервисов и Docker, я понимаю, что в свое время мы сделали правильный выбор — хотя на тот момент это казалось новым и непроверенным подходом.
Поэтому я рекомендую всем, кто начинает новый проект или хочет модифицировать старый, рассмотреть возможность использования микросервисов, Docker и разделения на модули.
Мы, если есть интерес, готовы и дальше делиться знаниями, которые мы приобрели за это время: о микросервисах на голанг , мониторинг и тестирование, на основе интерфейса РеактJS + Флюс и многое другое.
Теги: #docker #Микросервисы #архитектура #программирование #Анализ и проектирование систем #api
-
Вертяжница
19 Dec, 24 -
Еще Раз О «Налоге На Болванки»
19 Dec, 24 -
Большой Брат Внимательно Следит За Тобой?
19 Dec, 24