Как Groupon мигрировал от монолитного Rails приложения к новой Node.js инфраструктуре

в 14:24, , рубрики: groupon, node.js, ruby on rails, перевод, переводы, метки: , , ,

I-Tier: Расщепление монолита

Недавно мы завершили годовой проект миграции веб-трафика компании Групон в США от монолитного Ruby on Rails приложения к новому стеку Node.js и получили существенные результаты.

С самого начала весь веб-фронтенд американского Групона был единым исходным кодом Ruby. Код фронтенда быстро развивался, что затрудняло его поддержку и усложняло процесс добавления новых фич. В качестве решения проблемы с этом гигантским монолитом мы решили реструктурировать фронтенд посредством его разделения на меньшие, независимые и более простые в управлении части. Основой этого проекта стало разделение монолитного вебсайта на несколько независимых Node.js приложений. Мы также переделали инфраструктуру, чтобы обеспечить совместную работу всех приложений. Результатом стал Interaction Tier (I-Tier).

Вот некоторые из важных моментов этой глобальной архитектурной миграции:

• Страницы на сайте загружаются значительно быстрее

• Наши команды девелоперов могут разрабатывать и добавлять новые фичи быстрее и с меньшей зависимостью от других команд

• Мы можем избежать повторной разработки одних и тех же фич в разных странах, где доступен Групон.

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

Краткая история

Изначально Групон был одностраничным сайтом, который отображал ежедневные сделки жителям Чикаго. Примером такой типичной сделки мог быть дисконт в местный ресторан или билет на местный концерт.

У каждой сделки был “переломный момент” — минимальное количество людей, которые должны были купить сделку, чтобы она стала действительной. Если достаточное количество человек покупало сделку, достигающую переломного момента, все получали дисконт. В противном же случае все были в проигрыше.

Сайт изначально был создан как Ruby on Rails приложение. Rails приложение было хорошим выбором для начала, так как для нашей команды девелоперов это был один из простейших способов быстро поднять и запустить сайт. Кроме того, было довольно легко реализовать новые фичи с помощью Rails; на тот момент это было огромным плюсом для нас, так как набор фич постоянно увеличивался.

Изначальная архитектура Rails была достаточно проста:

image

Однако, мы быстро переросли возможность обслуживать весь наш трафик с помощью одного Rails приложения, направленного на один кластер базы данных. Мы добавили больше фронтенд серверов и реплик баз данных и разместили все за CDN, но это работало только до тех пор, пока записи в базу данных не стали критическим элементом. Обработка заказов стала причиной ряда записей в базу данных; в результате мы решили переместить этот код из нашего Rails приложения на новый сервис с его отдельным кластером баз данных.

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

image

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

Глобализация

Примерно в это время Групон начал расширяться на мировом уровне. За короткий период мы перешли от осуществления операций только на территории США к функционированию в 48 разных странах. В это время мы также приобрели несколько международных компаний, таких как CityDeal. Каждое приобретение уже включало в себя свой программный стек.

Архитектура CityDeal имела общие черты с архитектурой Групона, но это была полностью автономная реализация, созданная другой командой. В результате различия были в дизайне и технологии — Java вместо Ruby, Apache вместо nginx, PostgreSQL вместо MySQL.

image

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

Мобильные приложения

Мы также создали мобильные клиенты для iPhone, iPad, Android и Windows Mobile; мы определенно не хотели создавать отдельное мобильное приложение для каждой страны, в которой был доступен Групон. Вместо этого мы решили создать API слой поверх каждого из наших бэкенд платформ ПО; наши мобильные клиенты обращались к какому угодно API эндпоинту, соответствующему стране пользователя.

image

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

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

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

Составные монолиты

В то время как Групон продолжал развиваться и запускать новые продукты, количество исходного кода Ruby фронтенда увеличивалось. Слишком много разработчиков работало над одним и тем же кодом. Это дошло до того, что разработчикам стало сложно запускать приложение локально. Наборы тестов снижали темп, и ненадежные тесты стали проблемой. И так как это была одна кодовая база, целое приложение необходимо было срочно деплоить. Когда возникшая ошибка в продакшене требовала откат, вместо одной фичи изменения каждой команды были бы возвращены. Одним словом, у нас было множество проблем с монолитным кодом.

Но мы и раньше многократно сталкивались с подобными проблемами. Нам не только приходилось иметь дело с кодом в США, но у нас было много таких же проблем с европейским кодом. Нам необходимо было полностью реструктурировать фронтенд.

Переписать все

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

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

Цель 1: Объединить наши фронтенды

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

Цель 2: Поставить мобильные приложения на один уровень с вебом

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

Цель 3: Сделать сайт более быстрым

Наш сайт был медленнее, чем нам хотелось. Пока мы впопыхах пытались взять под контроль рост сайта, фронтэнд США накопил технический долг, из-за которого его было сложно оптимизировать. Нам хотелось найти такое решение, которое не требовало бы большого количества кода, чтобы обслуживать запросы. Мы хотели что-то простое.

Цель 4: Позволить командам продвигаться в работе независимо

Когда Групон впервые был запущен, сайт был действительно простым. Но с тех пор мы добавили множество новых продуктов с командами разработчиков для их поддержки по всему миру. Мы хотели, чтобы каждая команда была способна создавать и деплоить свои фичи независимо от других команд и быстро. Нам нужно было устранить взаимозависимость между командами, причиной которой было то, что все находилось в одном коде.

Подход

Сначала мы решили разбить каждую основную фичу сайта на отдельные веб приложения:

image

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

Примечание: почему Node.js?

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

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

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

Подход, продолжение...

Затем мы добавили слой маршрутизации сверху, который перенаправлял пользователей в соответствующее приложение, основанное на странице, которую они посещали:

image

Мы построили сервисы маршрутизации Групона (которые мы называем Grout) как модуль nginx. Это позволяет нам делать много интересных вещей, таких как проведение A / B тестов между различными реализациями одного и того же приложения на разных серверах.

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

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

image

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

image

Результаты:

Мы только что закончили переход нашего американского фронтэнда от Ruby к новой инфраструктуре Node.js. Старый монолитный фронтэнд был разделен примерно на 20 отдельных веб-приложений, каждое из которых было полностью переписано. Мы в настоящее время обслуживаем в среднем 50 тысяч запросов в минуту, но мы ожидаем увеличение этого трафика во время сезона отпусков. И это число будет значительно возрастать, так как мы перемещаем трафик от наших других 48 стран.

Это те преимущества, которые мы выявили за последнее время:

– Страницы загружаются быстрее — обычно на 50%. Отчасти это происходит из-за изменения технологии и отчасти потому, что у нас был шанс переписать все наши веб-страницы для ускорения работы. И мы все еще планируем добиться значительных сдвигов здесь, так как мы вносим дополнительные изменения.

– Мы обслуживаем такой же объем трафика с меньшим количеством оборудования по сравнению со старым стеком.

– Команды способны деплоить изменения в их приложениях независимо.

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

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

[ оригинал статьи на engineering.groupon.com ]

Автор: lisovna

Источник

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


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