Скажи «нет» монолитности: как мы делали CPA-платформу

в 7:20, , рубрики: cpa, Анализ и проектирование систем, архитектура системы, Блог компании Инфосистемы Джет
Скажи «нет» монолитности: как мы делали CPA-платформу - 1

Под катом — рассказ о том, как мы реализовали модульную архитектуру CPA-платформы (Content Provider Access) для телеком-оператора на Fuse Fabric. Заодно объясним, почему приняли такое решение, а не стали использовать стандартный стек технологий J2EE для создания монолитного приложения.

Что такое CPA

CPA — это платформа для организации взаимодействия с контент-провайдерами, которые предоставляют различные услуги абонентам своих партнёров, телеком-операторов.

В качестве простейшего сценария давайте рассмотрим ситуацию, когда абонент хочет узнать погоду на завтра:

  • абонент отправляет SMS-сообщение на короткий номер для получения прогноза погоды;
  • SMS-сообщение поступает в SMS-центр телеком-оператора;
  • SMS-центр отправляет сообщение в CPA-платформу;
  • CPA-платформа определяет услугу, привязанную к короткому номеру, определяет её стоимость, проверяет наличие на счету абонента достаточного количества денег, запрашивает у предоставляющего услугу партнера прогноз погоды, списывает деньги со счета абонента, протоколирует бизнес-операцию и передает данные в SMS-центр для отправки абоненту;
  • SMS-центр отправляет абоненту SMS-сообщение с погодой на завтра;
  • абонент получает SMS-сообщение.

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

Даже этот простейший сценарий состоит из более чем десятка операций. При этом у телеком-операторов могут быть десятки гораздо более сложных сценариев, в ходе которых взаимодействуют самые разные системы. Например, запрос может поступить из USSD-центра, IVR-центра, веб-портала и так далее. К тому же это может быть не разовый запрос, а подписка на услугу (в том числе в рассрочку), отписка от неё и так далее.

Выбор подхода

Когда мы начинали проект по созданию CPA-системы, то выбирали из нескольких вариантов архитектуры платформы. При проектировании приложений такого масштаба в первую очередь нужно учитывать следующие аспекты:

  • Это будет априори высоконагруженная система, которая должна обрабатывать сотни достаточно сложных бизнес-сценариев в секунду. При увеличении нагрузки система должна легко масштабироваться горизонтально.
  • На такие системы накладываются очень жёсткие требования по длительности простоя. Она должна быть минимальна даже при изменении конфигурации системы, например, в случае добавления новой функциональности или изменении текущих компонентов.
  • Необходимо обеспечить простое централизованное управление системы, с отображением метрик, позволяющих однозначно оценить текущее состояние.
  • Телеком-сектор требует очень быстрой реакции на изменения рынка и пула технологий, поэтому архитектура системы должна позволять быструю замену компонентов.
  • Крайне важно максимально сократить срок внедрения продукта, поэтому цикл разработки и тестирования новой функциональности должен быть как можно короче.

С учётом всего этого мы быстро осознали, что традиционная монолитная J2EE-архитектура нам не подходит, поскольку она не обеспечивает требуемой гибкости и не закрывает все потребности заказчика. Поэтому мы выбрали модульную архитектуру на основе OSGi — Red Hat Jboss Fuse в конфигурации Fuse Fabric.

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

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

При проектирования системы мы опирались на API модулей, из которых она должна была состоять, и BPM-процессы (бизнес-процессы, описанные с помощью стандартной нотации BPMN). Эти процессы посредством вызовов API модулей описывали бизнес-сценарии, которые выполняет CPA-платформа. То есть на начальном этапе у нас были модули (OSGi-наборы), по сути представлявшие собой микросервисы-«заглушки», выполняющие примитивные операции, и BPM-процессы, которые максимально приближенно к бизнес-сценариям телеком-оператора описывали последовательность вызовов OSGi-сервисов для достижения требуемого результата.

При этом мы понимали, что создаваемые нами OSGi-наборы будут вызываться как в BPM-сценариях, так и из других мест. Например, сервис для работы с каталогом услуг не только непосредственно вызывается из BPM-процессов, но и используется в back-office-системах, чтобы контент-менеджеры могли заводить, удалять и редактировать услуги. Также API сервисного каталога используется для предоставления списка услуг на сайте телеком-оператора. Сервис работы с биллинговой системой сначала использовался только в CPA-платформе, но теперь он доступен и в виде REST-сервиса, поэтому его услугами пользуются и другие системы в инфраструктуре телеком-оператора.

Реализация

При создании архитектуры в виде модулей с четко специфицированным API мы очень быстро убедились её эффективности и работоспособности.

Поскольку API OSGi-наборов был согласован и утверждён, мы смогли распараллелить процесс разработки отдельных OSGi-наборов по разным командам разработчиков. При этом команды никак не влияли друг на друга, потому что все они работали со своими кодовыми базами и предметными областями. Главное — не нарушать согласованный API-набор. Одна команда могла сосредоточиться на разработке партнёрского каталога, другая — на разработке сервиса бизнес-логирования, и так далее. При этом архитектор следил за целостностью системы на основе согласованных API.

Для простоты отслеживания изменений API мы взяли на вооружение семантическое версионирование наборов OSGi. Этот подразумевает присвоение версий компонент в формате X.Y.Z, где X — мажорная версия, Y — минорная версия, Z — патч-версия. При этом версии итерируются по следующим правилам:

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

Благодаря этому мы легко отслеживали изменения API сервисов в рамках всей системы и безошибочно управляли ими при выстраивании совместной работы сервисов.

При разработке сложных систем необходимо писать тесты на реализованную функциональность. Поскольку компоненты в среде OSGi взаимодействуют с помощью вызова интерфейсов, это значительно упрощает модульное тестирование. Если какой-либо сервис в нашем приложении обращается к другому сервису, то на этапе тестирования мы можем легко заменять «заглушкой» тот сервис, к которому происходит обращение. И при тестировании каждого модуля необходимо учитывать специфику его работы.

Мы понимали, что в высоконагруженной системе любой сервис, в зависимости от характера нагрузки, может стать узким местом всего приложения. Поэтому для каждого вызова API какого-либо OSGi-набора снимались метрики, которые были доступны по JMX-интерфейсу. Это позволяло регистрировать максимальную продолжительность вызова какого-либо метода сервиса, усредненное значение за промежуток времени и динамику изменения метрики. Снимаемые данные помогли нам во время нагрузочного тестирования сразу идентифицировать узкие места приложения, а также дали возможность наблюдать за production-системой в онлайн-режиме. Так что при деградации производительности мы могли оперативно принимать меры по стабилизации системы. Например, можно перенести сервис на другую ноду runtime-среды, или вынести в отдельную JVM. При этом OSGi позволяет нам выполнять все эти действия на работающей системе без влияния на остальные компоненты приложения.

При добавлении новой функциональности в существующую систему мы можем явно отслеживать изменения в кодовой базе (новые наборы или наборы более поздней версии) и оптимизировать процесс тестирования. Если мы видим, что в очередной поставке версия компонента не изменилась, то для него не нужно разрабатывать новые тесты и повторно тщательно его тестировать. Лучше сосредоточиться на новой функциональности, добавляемой в систему.

Конечно же, никто не застрахован от ошибок, которые могут возникнуть в production-среде. Но OSGi позволяет оперативно вносить изменения в работающую систему. Достаточно исправить ошибку в компоненте, инкрементировать его патч-версию и установить в среду исполнения OSGi. Это важное преимущество в условиях, когда длительность простоя системы значительно влияет как на качество предоставляемого сервиса, так и на прибыль компании.

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

Отдел разработки Центра программных решений компании «Инфосистемы Джет»

Автор: JetHabr

Источник


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


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