К методике извлечения микросервисов из монолитных корпоративных систем

Алессандра Левковиц1, Рикардо Терра2, Марко Тулио Валенте1
1 Федеральный университет Минас-Жерайс (UFMG), Белу-Оризонти, Бразилия
2 Федеральный университет Лавраса (UFLA), Лаврас, Бразилия
alessandralev@gmail.com, terra@dcc.ufla.br, mtov@dcc.ufmg.br

Аннотация

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

1. Введение

Монолитные системы неизбежно становятся больше со временем, отклоняясь от своей предполагаемой архитектуры и становясь сложными, рискованными и дорогостоящими в развитии [6; 8]. Несмотря на эти проблемы, корпоративные системы часто принимают монолитные архитектурные стили. Следовательно, основной проблемой в настоящее время в разработке корпоративного программного обеспечения является развитие монолитной системы в сжатые бизнес-сроки, с целевым бюджетом, но с сохранением качества, доступности и надёжности [3].

Недавно микросервисная архитектура появилась как альтернатива для развития монолитных приложений [2]. Архитектура предлагает разрабатывать систему как набор связных сервисов, которые развиваются с течением времени и могут независимо разрабатываться и развёртываться. Микросервисы организованы как набор небольших сервисов, где каждый работает в своём собственном процессе и взаимодействует через облегчённые механизмы. Эти сервисы строятся вокруг бизнес-возможностей и развёртываются независимо [5]. Таким образом, каждый сервис может быть разработан на языке программирования, который лучше подходит для характеристик сервиса, может использовать наиболее подходящий механизм сохранения данных, может работать в соответствующей аппаратной и программной среде и может разрабатываться разными командами. Существует множество недавних историй успеха использования микросервисов в известных компаниях, таких как Amazon и Netflix [7].

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

В этой статье мы описываем методику идентификации микросервисов в монолитных системах. Мы успешно применили эту методику на реальной монолитной банковской системе объемом 750 KLOC, которая управляет транзакциями с 3,5 миллионов банковских счетов и выполняет почти 2 миллиона авторизаций в день. Наша оценка показывает, что предлагаемая методика способна идентифицировать микросервисы в монолитной системе и что микросервисы могут быть многообещающей альтернативой для модернизации унаследованных монолитных корпоративных систем.

Остальная часть этой статьи организована следующим образом. Раздел 2 описывает предлагаемую методику идентификации микросервисов в монолитных системах. Раздел 3 оценивает предлагаемую методику на реальной монолитной банковской системе. Раздел 4 представляет сопутствующие работы, и Раздел 5 завершает статью.

2. Предлагаемая методика

Предлагаемая методика учитывает, что монолитные корпоративные приложения имеют три основные части [6]: пользовательский интерфейс на стороне клиента, приложение на стороне сервера и базу данных, как показано на Рисунке 1. Она также учитывает, что большая система структурирована на более мелкие подсистемы, и каждая подсистема имеет чётко определённый набор бизнес-обязанностей [1]. Мы также предполагаем, что каждая подсистема имеет отдельное хранилище данных.

В формальных терминах мы предполагаем, что система S представлена тройкой \((\text{F},\text{B},\text{D})\), где \(\text{F}=\{\text{fc}_{1},\text{fc}_{2},\ldots,\text{fc}_{n'}\}\) — это набор фасадов, \(\text{B}=\{\text{bf}_{1},\text{bf}_{2},\ldots,\text{bf}_{n''}\}\) — это набор бизнес-функций, и \(\text{D}=\{\text{tb}_{1},\text{tb}_{2},\ldots,\text{tb}_{n'''}\}\) — это набор таблиц базы данных. Фасады (\(\text{fc}_{i}\)) — это точки входа системы, которые вызывают бизнес-функции (\(\text{bf}_{i}\)). Бизнес-функции — это методы, которые кодируют бизнес-правила и зависят от таблиц базы данных (\(\text{tb}_{i}\)). Также важно определить организацию предприятия \(\text{O}=\{\text{a}_{1},\text{a}_{2},\ldots,\text{a}_{v}\}\), разделённую на бизнес-области \(\text{a}_{i}\), каждая из которых отвечает за бизнес-процесс.

Мы описываем нашу методику идентификации микросервисов в системе S следующими шагами:

Рисунок 1: Монолитное приложение

Рисунок 1: Монолитное приложение

Шаг №1

Сопоставьте таблицы базы данных \(\mathtt{tb_{i}} \in \mathbb{D}\) с подсистемами \(\mathtt{ss_{i}} \in \mathbb{SS}\). Каждая подсистема представляет бизнес-область (\(\mathtt{a_{i}}\)) организации O. Например, как представлено на Рисунке 2, подсистема \(\mathbb{SS}_{2}\), которая представляет бизнес-область \(\mathtt{a_{2}}\), зависит от таблиц базы данных \(\mathtt{tb_{3}}\) и \(\mathtt{tb_{6}}\). Таблицы, не связанные с бизнес-процессом — например, таблицы сообщений об ошибках и журналов — классифицируются в специальной подсистеме, называемой Управляющая Подсистема (SSC).

Шаг №2

Создайте граф зависимостей \((V,E)\), где вершины представляют фасады (\(\text{fc}_{i} \in F\)), бизнес-функции (\(\text{bf}_{i} \in B\)) или таблицы базы данных (\(\mathtt{tb_{i}} \in \mathbb{D}\)), а рёбра представляют: (i) вызовы от фасадов к бизнес-функциям; (ii) вызовы между бизнес-функциями; и (iii) доступы от бизнес-функций к таблицам базы данных. Рисунок 3 иллюстрирует граф, где фасад \(\text{fc}_{2}\) вызывает бизнес-функцию \(\text{bf}_{2}\) (случай \(i\)), бизнес-функция \(\text{bf}_{2}\) вызывает бизнес-функцию \(\text{bf}_{4}\) (случай \(ii\)), и бизнес-функция \(\text{bf}_{4}\) обращается к таблицам базы данных \(\mathtt{tb_{2}}\) и \(\mathtt{tb_{3}}\) (случай \(iii\)).

Шаг №3

Идентифицируйте пары \((\text{fc}_{i},\mathtt{tb_{j}})\), где \(\text{fc}_{i} \in F\) и \(\mathtt{tb_{j}} \in \mathbb{D}\), и существует путь от \(\text{fc}_{i}\) к \(\mathtt{tb_{j}}\) на графе зависимостей. Например, в графе, проиллюстрированном на Рисунке 3, мы идентифицируем пары \((\text{fc}_{1},\mathtt{tb_{1}})\), \((\text{fc}_{2},\mathtt{tb_{1}})\), \((\text{fc}_{2},\mathtt{tb_{2}})\), \((\text{fc}_{2},\mathtt{tb_{3}})\), и \((\text{fc}_{2},\mathtt{tb_{4}})\).

Шаг №4

Для каждой подсистемы \(\mathtt{ss_{i}}\), определённой ранее в Шаге №1, выберите пары \((\text{fc}_{i},\mathtt{tb_{j}})\), идентифицированные на предыдущем шаге, где \(\mathtt{tb_{j}} \in \mathtt{ss_{i}}\). Например, в нашем иллюстративном примере, \(\mathtt{ss_{3}}=\{\mathtt{tb_{2}},\mathtt{tb_{4}}\}\), тогда мы выбираем пары \((\text{fc}_{2},\mathtt{tb_{2}})\) и \((\text{fc}_{2},\mathtt{tb_{4}})\).

Шаг №5

Идентифицируйте кандидатов для преобразования в микросервисы. Для каждой отдельной пары \((\text{fc}_{i},\mathtt{tb_{j}})\), полученной на предыдущем шаге, мы проверяем код фасада и бизнес-функций, которые находятся на пути от вершины \(\mathtt{fc_{i}}\) к \(\mathtt{tb_{j}}\) в графе зависимостей. Например, для пары \((\mathtt{fc_{2}},\mathtt{tb_{2}})\), мы проверяем фасад \(\mathtt{fc_{2}}\) и бизнес-функции \(\mathtt{bf_{2}}\) и \(\mathtt{bf_{4}}\). Проверка направлена на идентификацию того, какие бизнес-правила фактически зависят от таблицы базы данных \(\mathtt{tb_{j}}\), и такие операции должны быть описаны в текстовой форме как правила. Следовательно, для каждой пары \((\mathtt{fc_{i}},\mathtt{tb_{j}})\), кандидат микросервис (M) определяется следующим образом:

Шаг №6

Создайте API-шлюзы, чтобы сделать миграцию на микросервисы прозрачной для клиентов. API-шлюз состоит из промежуточного слоя между стороной клиента и приложением на стороне сервера. Это новый компонент, который обрабатывает запросы от стороны клиента — на той же технологии и интерфейсе, что и \(\mathtt{fc_{i}}\) — и синхронизирует вызовы к новому микросервису M и к \(\mathtt{fc'_{i}}\) — новой версии \(\mathtt{fc_{i}}\) без кода, который был извлечён и реализован в микросервисе M. API-шлюз должен быть определён для каждого фасада.

Есть три случая синхронизации, которые мы должны рассмотреть в нашей оценке: (i) когда вход \(\mathtt{fc'_{i}}\) является выходом M или вход M является выходом \(\mathtt{fc'_{i}}\); (ii) когда вход M и \(\mathtt{fc'_{i}}\) одинаковы как вход API-шлюза и порядок инстанцирования несущественен; и (iii) когда мы должны разделить \(\mathtt{fc_{i}}\) на две функции \(\mathtt{fc'_{i}}\) и \(\mathtt{fc''_{i}}\) и микросервис M должен быть вызван после \(\mathtt{fc'_{i}}\) и перед \(\mathtt{fc''_{i}}\).

С одной стороны, если мы можем синхронизировать вызовы, как описано в случае (i) или (ii), мы идентифицируем предлагаемый микросервис M как "сильного кандидата". С другой стороны, если мы можем синхронизировать вызовы только как в случае (iii), мы идентифицируем предлагаемый микросервис как "кандидата с дополнительными усилиями". В частности, в нашей методике, предполагая микросервис подсистемы \(\mathtt{ss_{x}}\), если мы идентифицируем бизнес-правило в определении микросервиса, которое нуждается в обновлении данных в \(\mathtt{tb_{i}} \in \mathtt{ss_{x}}\) и \(\mathtt{tb_{j}} \not\in \mathtt{ss_{x}}\) в той же транзакционной области, мы идентифицируем предлагаемый микросервис как "не кандидата".

Когда каждая оцененная пара \((\mathtt{fc_{i}},\mathtt{tb_{j}})\) подсистемы классифицирована как кандидат микросервиса, мы рекомендуем мигрировать всю подсистему на новую архитектуру. В этом случае мы должны реализовать идентифицированные микросервисы, создать независимую базу данных с таблицами подсистемы, разработать API-шлюзы и устранить реализацию подсистемы (исходный код и таблицы) из системы S. Хотя API-шлюзы должны быть развёрнуты

на том же сервере, что и система S, чтобы избежать воздействий на клиентский слой, микросервисы могут быть разработаны с использованием любой технологии и развёрнуты где угодно, где это более подходит.

Рисунок 2: Декомпозиция базы данных

Рисунок 2: Декомпозиция базы данных

Рисунок 3: Граф зависимостей

Рисунок 3: Граф зависимостей

3. Оценка

Мы применили нашу предлагаемую методику на большой системе из бразильского банка. Система обрабатывает транзакции, выполняемые клиентами на множественных банковских каналах (Интернет-банкинг, Колл-центр, Банкоматы, POS-терминалы и т.д.). Она имеет 750 KLOC на языке C и работает на многоядерных серверах Linux. Система полагается на СУБД с 198 таблицами, которая выполняет, в среднем, 2 миллиона транзакций в день.

Шаг №1

Мы идентифицировали 24 подсистемы, включая подсистему SSC. Таблица 1 показывает фрагмент результата, полученного после этого начального шага. Заголовки представляют подсистемы, и их содержание представляет таблицы, от которых они зависят. Одна проблема, которую мы идентифицировали, заключается в том, что определённые таблицы — выделенные серым — связаны более чем с одной подсистемой.

Таблица 1: Сопоставление подсистем и таблиц

Бизнес-действия Плата за услуги Чеки Клиенты Текущие счета Сберегательные счета Социальные льготы Предварительно одобренный кредит Дебетовые и кредитные карты SMS канал
ACO AGT CHS CLT CNT CNT BEN LPA CMG CTS
ACB ISE TCE CCT CPO DPB INP STS
RCA PTC ECH RCC LAN DBC NPP CMS
PTF HET LAN RCC IBS BIN RCS
RTE CCF CCO MPO LBE CCM RLS
RTT CCE PPO PBE RTS
TPT CHE SPA
TTE
UTM
DUT

Шаг №2

Мы создали граф зависимостей, состоящий из 1,942 вершин (613 фасадов, 1,131 бизнес-функций и 198 таблиц базы данных), 5,178 рёбер, представляющих вызовы функций, и 2,030 рёбер, соответствующих доступам к таблицам базы данных. Из-за размера нашей оцениваемой системы мы продолжили нашу оценку для следующих пяти подсистем: Бизнес-действия, Плата за услуги, Чеки, Клиенты и SMS канал. Таблица 2 иллюстрирует характеристики каждой подсистемы. Для подсистемы Бизнес-действия мы получили граф, представленный на Рисунке 4.

Таблица 2: Оцениваемые подсистемы

Подсистема Бизнес-действия Плата за услуги Чеки SMS канал Клиенты
Таблицы (вершины) 3 10 5 6 1
Функции (вершины) 5 62 29 138 >150
Вызовы функций (рёбра) 3 79 14 133 >150
Доступы к БД (рёбра) 6 14 22 140 26
Кандидаты микросервисов 1 3 8 4 4

Шаги №3-№4

Рассматривая подсистему Бизнес-действия, мы находим следующие пары:

(AUTCCErspSo1AutCceNov, ACO), (AUTCCErspSo1AutCceNov, RCA), (AUTPOSrspIdePosTpgCmgQ1q, ACO), (AUTPOSrspIdePosTpgCmgQ1q, RCA), (AUTPOSrspIdePosTpgCmgQ1q, ACB).

Шаг №5

Для пар (AUTCCErspSo1AutCceNov, ACO), (AUTCCErspSo1AutCceNov, RCA), полученных на предыдущем шаге, мы проверяем код фасада AUTCCErspSo1AutCceNov и бизнес-функций, которые он вызывает (AUTCCEobtAccCom), и мы идентифицировали микросервис, описанный следующим образом:

Мы также оценили другие три пары (AUTPOSrspIdePosTpgCmgQ1q, ACO), (AUTPOSrspIdePosTpgCmgQ1q, RCA), (AUTPOSrspIdePosTpgCmgQ1q, ACB) и бизнес-функции, которые они вызывают AUTPOScltBen и AUTPOScltCmuEspCpp, и мы идентифицировали те же микросервисы, как описанный выше. Фактически, таблица ACB могла бы быть объединена с ACO.

Шаг №6

Для фасадов AUTCCErspSo1AutCceNov и AUTPOSrspIdePosTpgCmgQ1q мы идентифицировали API-шлюз, который подходит случаю (\(i\)), описанному в предлагаемой методике.

Мы также оценили шаги №3–№6 для подсистем: Плата за услуги, Чеки, Клиенты и SMS канал. Хотя подсистема Плата за услуги имеет 10 таблиц и 51 фасад, мы только идентифицировали и определили следующие три микросервиса: ServiceCharge.CalculateServiceCharge, ServiceCharge.IncrementServiceChargeUsage, и ServiceCharge.DecrementServiceChargeUsage. С одной стороны, мы идентифицировали 14 API-шлюзов, которые также подходят случаю (\(i\)). С другой стороны, мы идентифицировали другие 37 API

которые подходят случаю (\(ii\)). Однако мы можем избежать разработки последних 37 API-шлюзов, поскольку вызываемые микросервисы имеют только входные данные и могут быть реализованы с асинхронным запросом. Таким образом, в частности, в этом случае, мы предлагаем использовать менеджер очередей сообщений (MQM) для коммуникации и заменить C-код, который выполняет обновление в таблице базы данных, на операцию "put" в очереди.

Для подсистем Чеки и SMS канал мы идентифицировали микросервисы и API с теми же характеристиками, что и подсистема Плата за услуги, что указывает на то, что обе подсистемы являются хорошими кандидатами для миграции на микросервисы. Тем не менее, для подсистемы Клиенты — которая обращается только к одной таблице — мы идентифицировали один микросервис, который должен вызываться более чем 50 API-шлюзами, которые подходят случаю (\(iii\)). Следовательно, мы не рекомендовали её миграцию на микросервисы, поскольку усилия по разделению и перемодуляризации более чем 50 функций, созданию и поддержке более чем 50 API-шлюзов, вероятно, больше, чем преимущества реализации микросервиса.

И последнее, но не менее важное: мы игнорируем подсистемы, которые имеют одну или более таблиц, которые появляются в списке более чем одной подсистемы, такие как таблица CNT подсистемы Текущие счета (см. Таблицу 1), потому что наша методика идентификации микросервисов учитывает, что только одна подсистема обрабатывает операции с каждой таблицей.

Рисунок 4: Граф подсистемы Бизнес-действия

Рисунок 4: Граф подсистемы Бизнес-действия

Краткое обсуждение

Мы классифицируем наше исследование как успешное, потому что мы смогли идентифицировать и классифицировать все подсистемы, создать и проанализировать граф зависимостей, который значительно помог идентифицировать кандидатов в микросервисы. В качестве нашего практического результата, мы рекомендовали мигрировать 4 из 5 оцениваемых подсистем на микросервисную архитектуру.

5. Заключение и будущая работа

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

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

Однако есть подсистемы, которые не были классифицированы как хорошие кандидаты для миграции на микросервисы. Мы нашли сценарии, которые потребовали бы значительных дополнительных усилий для миграции подсистемы на набор микросервисов. Например, (i) подсистемы, которые разделяют одну и ту же таблицу базы данных, (ii) микросервис, который представляет операцию, которая всегда находится в середине другой операции, и (iii) бизнес-операции, которые вовлекают более одной бизнес-подсистемы в транзакционной области (например, перевод денег с чекового счета на сберегательный счет).

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

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

Благодарности

Наше исследование было поддержано CAPES, FAPEMIG и CNPq.

Литература

  1. Melvin E Conway. How do committees invent? Datamation, 14(4):28-31, 1968.
  2. Martin Fowler and James Lewis. Microservices. http://martinfowler.com/articles/microservices.html, 2014.
  3. M Lynne Markus and Cornelis Tanis. The enterprise systems experience-from adoption to success. Framing the domains of IT research: Glimpsing the future through the past, 173:207-173, 2000.
  4. Dmitry Namiot and Manfred Sneps-Sneppe. On micro-services architecture. International Journal of Open Information Technologies, 2(9):24-27, 2014.
  5. Sam Newman. Building Microservices. O'Reilly Media, 2015.
  6. Chris Richardson. Microservices: Decomposing applications for deployability and scalability. http://www.infoq.com/articles/microservices-intro, 2014.
  7. Chris Richardson. Pattern: Microservices architecture. http://microservices.io/patterns/microservices.html, 2014.
  8. Santonu Sarkar, Shubha Ramachandran, G. Sathish Kumar, Madhu K. Iyengar, K. Rangarajan, and Saravanan Sivagnanam. Modularization of a large-scale business application: A case study. IEEE Software, 26:28-35, 2009.
  9. Ricardo Terra, Marco Tulio Valente, and Roberto S. Bigonha. An approach for extracting modules from monolithic software architectures. IX Workshop de Manutencao de Software Moderna (WMSWM), pages 1-18, 2012.