Модернизация унаследованных корпоративных систем — вопрос, с которым сталкиваются многие организации, планирующие использовать новые облачные технологии для обеспечения высокого уровня масштабируемости и готовности. Если исходный код унаследованной системы доступен и допускает внесение изменений, то микросервисы становятся многообещающим решением, при котором централизованные сервисы заменяются несколькими независимыми [1, 2] (рис. 1). Микросервисы позволяют проводить пошаговую модернизацию, что способствует созданию систем с высоким уровнем масштабируемости и готовности [3] (за счет избыточности экземпляров сервисов) и ведет к сокращению затрат. Применение микросервисов открывает возможность разбивать процесс модернизации на небольшие этапы, что зачастую оказывается предпочтительнее одномоментного внесения крупномасштабных изменений.

Рис. 1. Сравнение (a) унаследованной (монолитной) архитектуры и (б) архитектуры микросервисов

 

Мультиарендность

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

Рис. 2. Архитектура мультиарендного корпоративного программного обеспечения, предоставляемого в качестве сервиса

 

 

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

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

Процедуры, обрабатывающие входные данные с информацией о клиенте (например, запросы сервиса, ввод через пользовательский интерфейс, операции чтения из базы данных), должны извлекать клиентский идентификатор из исполняемой среды и присваивать его входному каналу. Например, если за размещение мультиарендного микросервиса отвечает Google App Engine, идентификатор арендатора извлекается из домена Google Apps.

Аналогичным образом процедуры, обрабатывающие выходные данные с информацией о клиенте (например, ответы сервиса, вывод через пользовательский интерфейс и операции записи в базу данных), извлекают клиентский идентификатор и присваивают его выходному каналу. Windows Azure, к примеру, перед обращением к API хранилища требует определения клиентского контекста.

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

 

Проблемы перехода на микросервисы

Мультиарендность — способность системы

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

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

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

 

Сохранение состояния

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

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

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

Рис. 3. Готовность и устойчивость при использовании избыточных (не зависящих от состояния) микросервисов

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

Готовность = MTBF / (MTBF + MTTR), где MTBF (mean time between failures) — средний промежуток времени между отказами, а MTTR (mean time to repair) — среднее время восстановления. Предположим, что все экземпляры микросервисов имеют одинаковый параметр MTBF, тогда готовность системы возрастает при снижении MTTR. Этого можно достичь за счет предоставления балансировщику нагрузки возможности останавливать переадресацию входящих клиентских запросов сбойным экземплярам микросервиса и направлять их нормально функционирующим экземплярам. Балансировщик нагрузки способен немедленно направлять запросы уже развернутым избыточным микросервисам только в том случае, если они не зависят от состояния.

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

Устойчивость = Количество успешных запросов / Общее количество запросов × 100%.

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

В контексте извлечения микросервисов унаследованная программа зависит от состояния, если она способна сохранять значения переменных (то есть состояния) между вызовами и генерировать результаты, которые зависят не только от текущих входных параметров, но и от ранее сохраненного состояния.

При анализе микросервисом состояния унаследованного кода требуется проверить включение или исключение определений переменных состояния (то есть состояние сеанса в памяти).

Процедуры и объекты с сохранением состояния

Унаследованный программный код может сохранять состояние на уровне процедуры, на уровне объекта или на уровне компонента. Процедура с сохранением состояния возвращает значения, зависящие от переменных, время жизни которых превышает время выполнения процедуры, — переменных, которые не инициализируются между вызовами процедуры. В языке PHP, например, процедура с сохранением состояния может быть реализована путем объявления внутри нее статической переменной. В большинстве языков этой же цели можно достичь, объявив глобальную переменную вне процедуры. Сохранения состояния в процедуре можно добиться, вызывая другую процедуру с сохранением состояния и возвращая значения ее переменных.

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

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

Компоненты с сохранением состояния

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

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

Перенос унаследованного программного кода с сохранением состояния в микросервис

Перенос унаследованного кода в микросервисы может осуществляться сверху вниз, снизу вверх или путем деления пополам, в том числе с рефакторингом унаследованного кода и применением архитектурных решений к нужному микросервису. Используемые в дальнейшем шаблоны сервисной архитектуры (service-oriented architecture, SOA) ориентированы на сохранение состояния и применимы к микросервисным архитектурам.

Шаблон Stateful Messaging делегирует данные внутреннего состояния сообщениям микросервиса — запросам и ответам сервиса. Унаследованные процедуры с сохранением состояния могут быть реализованы без учета состояния путем замены локальных переменных состояния дополнительными параметрами и возврата значений, позволяющих определять состояние и возвращать его. Затем эти дополнительные параметры состояния и возвращаемые значения связываются соответственно с запросами и ответами микросервиса. Объекты и компоненты с сохранением состояния могут быть подвергнуты рефакторингу аналогичным образом.

Шаблон Partial State Deferral представляет собой опцию микросервиса, позволяющую сохранять состояние частично, с тем чтобы уменьшить расход памяти.

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

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

Согласованность данных

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

Выявление вопросов согласованности данных в унаследованном коде

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

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

Реструктуризация унаследованного кода с согласованными данными

У микросервисов имеется собственное хранилище данных — выделенный экземпляр базы данных, выделенная схема в общей базе данных или выделенные таблицы базы данных. Это позволяет решать вопросы согласованности при синхронизации данных микросервиса с централизованной базой данных.

Шаблон SOA Service Data Replication реплицирует данные в сервис базы данных. Его можно безопасно применять к операциям, допускающим только чтение, однако операции чтения-записи требуют дополнительных проверок согласованности данных.

Облачные шаблоны Strict Consistency и Eventual Consistency помогают решать вопросы согласованности в облачных хранилищах. Шаблон Strict Consistency позволяет считывать и записывать переменное число реплик данных. Например, в системе, включающей n реплик данных, операции записи могут обращаться к w репликам, а операции чтения — к r репликам. Для каждой операции гарантируется отношение n < w + r, а строгая согласованность обеспечивается за счет числа реплик чтения или записи, при которых каждая из операций обращается по крайней мере к одной актуальной версии данных.

Рис. 4. Шаблон Eventual Consistency. Несогласованность устраняется путем многоэтапной синхронизации реплик

Шаблон Eventual Consistency применяется в неустойчивых сетях или сетях с ограниченной пропускной способностью, а также при больших объемах данных, когда невозможен одновременный доступ к нескольким репликам (рис. 4). Операциям чтения-записи доступна только одна реплика, что приводит к временной несогласованности данных ради повышения уровня готовности и производительности. В конечном итоге несогласованность устраняется путем синхронизации реплик данных. Несогласованность данных может сохраняться, если записи данных в различных репликах изменяются одновременно. В этом случае для разрешения конфликтов разрабатываются определенные правила — например, одна из измененных версий просто отбрасывается. Такие правила допустимы только в некритичных к данным системах, а в важных корпоративных системах в этих случаях выполняется откат или производятся какие-то другие компенсационные действия.

***

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

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

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

Литература

  1. O. Zimmermann. Microservices Tenets // Computer Science — Research and Development. — 2017. vol. 32, N. 3–4. — P. 301–310.
  2. C. Pautasso et al. Microservices in Practice, Part 1: Reality Check and Service Design // IEEE Software. — 2017. vol. 34, N. 1. — P. 91–98.
  3. I. Gorton. Hyper Scalability — the Changing Face of Software Architecture. Software Architecture for Big Data and the Cloud, Elsevier, 2017. — P. 13–31.

Андрей Фурда (  andreifurda@gmail.com   ) —  лектор научно-инженерного факультета, Олаф Циммерманн  —  профессор и партнер Университета прикладных наук Восточной Швейцарии в Рапперсвиле, входит в редакционныКолин Фидж c.fidge@qut.edu.au )  —  профессор научно-инженерного факультета Квинслендского технологического университета; й совет IEEE Software; Уэйн Келли ( w.kelly@qut.edu.au )  —  старший лектор, Алистер Баррос ( alistair.barros@qut.edu.au )  —  профессор Школы информационных систем Квинслендского технологического университета.

 

Andrei Furda, Colin Fidge, Olaf Zimmermann, Wayne Kelly, Alistair Barros. Migrating Enterprise Legacy Source Code to Microservices. On Multitenancy, Statefulness, and Data Consistency, IEEE Software, May/June 2018, IEEE Computer Society. All rights reserved. Reprinted with permission.