Привет, Хабр!
Продолжая проработку темы микросервисов, мы решили предложить вам перевод статьи о тестировании MOA (микросервисно-ориентированных приложений). В последнее время мы уже обращались к теме тестирования, но в случае микросервисов обычного модульного тестирования недостаточно, необходимо также учитывать аспекты, связанные с CI/CD и другие вещи, о которых пойдет речь в этой статье.
Микросервисы – новый стиль программной архитектуры, применяемый при создании распределенных систем и все шире внедряемый в компаниях, работающих в Вебе, в том числе, в крупнейших из них. Так, Netflix и Amazon взяли на вооружение микросервисную архитектуру, чтобы значительно ускорить релиз программных продуктов. При этом более древние монолитные системы не могут масштабироваться со скоростью, которая отвечала бы современным вызовам.
Но та польза, которую может принести микросервисная архитектура, хороша лишь настолько, насколько полно удается протестировать практические методы для ее поддержки. При проектировании тестов для проверки микросервисных систем, где темп релизов можно назвать молниеносным, требуется придерживаться новаторского мышления, как на микро- так и на макроуровне.
Далее мы исследуем три типа микросервисно-ориентированных приложений, обсудим некоторые вызовы, с которыми придется справиться, чтобы наладить их тестирование, а также поговорим о том, как пишутся тесты, позволяющие наиболее полно оценить достоинства микросервисов.
Прежде чем переходить к некоторым тонкостям проектирования тестов для микросервисов, давайте вспомним, что представляют собой микросервисы. Микросервис – это детально выделенный программный компонент, которому дано четкое семантическое определение; при этом микросервис несет собственный набор данных и не зависит от других структур данных и источников данных. У каждого микросервиса свой цикл развертывания, поэтому после того, как в микросервис были внесены изменения, можно выполнить его релиз, не прерывая работы никаких других микросервисов в области действия того приложения, с которым работает этот микросервис.
Также давайте рассмотрим, чем микросервис не является. На следующем рисунке показан пример типичного монолитного приложения.
Рис. 1: В архитектурах монолитных приложений обычно наблюдается сильная связанность компонентов
Компоненты Customers (Клиенты), Products (Товары), Orders (Заказы) и (Комментарии) можно трактовать как множества классов, написанных на объектно-ориентированном языке, например, на Java или C#, где customers – это массив объектов, соответствующих клиентам, products – массив объектов, соответствующих товарам и т.д. Объект customer может воспользоваться как объектом customer, так и объектом product. Либо в силу того, что все компоненты монолитного приложения обращаются к одной и той же базе данных, объект order вполне может обратиться напрямую к базе данных и получить всю информацию о товаре и заказе, которая нужна для выполнения поставленных задач. Прямой доступ к базе данных не только возможен – притом, насколько он противоречит духу объектно-ориентированного программирования на основе объектно-реляционного отображения ORM — но и активно используется, особенно в тех сферах, где существует высокий спрос на готовые фичи к условленному сроку, не важно, какой ценой.
В результате получается сильно связанная, порой хрупкая система, где релиз новой версии приложения требует значительной координации между всеми командами разработчиков, участвующими в развитии этой системы. Из-за сильной связанности весь релизный цикл не может идти быстрее, чем самая медленная работа по пересмотру какой-нибудь зависимости. Иными словами, если приложение пора обновлять и вносить в него одновременно новую фичу по клиентам и новую фичу по товарам, то релиз не состоится, пока обе эти фичи не будут готовы. Если на внесение обновления по товарам требуется один день, а на внесение изменения по клиентам требуется три недели, то релиз состоится не ранее чем через три недели. Причем, что еще сильнее усугубляет ситуацию, при релизе может потребоваться не только скоординировать новый код, касающийся клиентов и товаров, но и внести изменения в схему базы данных, и этим также придется управлять в процессе релиза.
Релиз изменений для базы данных – очень деликатное предприятие. При изменении структуры базы данных всегда существует риск непредусмотренных побочных эффектов. Помните, что, если любой компонент может обращаться к базе данных, то он и будет к ней обращаться, причем, даже производить над данными такие операции, которые не входят в зону ответственности данного компонента. Такие «манипуляции вне зоны ответственности» могут приводить к сбоям других компонентов, и подобные сбои могут оставаться незамеченными, пока не произойдет катастрофа. Иногда потенциальная опасность может обнаружиться в коде вплоть до выхода этого кода в продакшен. К сожалению, такое происходит постоянно.
Некоторые компании готовы мириться с медленными релизными циклами, какие характерны для работы с монолитными приложениями. Но, если речь идет о компании, обеспечивающей поддержку сотен тысяч пользователей, и должна при этом поддерживать в работоспособном виде тысячи компонентов, то темп «не быстрее релиза самого запаздывающего компонента» неприемлем.
В микросервисно-ориентированном приложении (MOA) каждый функциональный компонент разлагается на минимально возможные единицы, обладающие выраженным функционалом (в разумных пределах). В таком случае каждый такой функциональный компонент организуется в виде микросервиса. В каждой компании есть свой багаж унаследованного кода, а также корпоративная культура, которой следует придерживаться, поэтому невозможно расписать «как по нотам», как именно должна осуществляться декомпозиция.
Когда возникает необходимость декомпозировать монолитное приложение в MOA, главное помнить, что лучшее – враг хорошего. Убедитесь, что каждый из микросервисов структурирован согласно его семантическому определению, что для микросервиса предусмотрены собственные данные, а также собственный релизный цикл. Глубина реализации зависит от того, что может себе позволить компания, исходя из имеющегося времени, опыта сотрудников и доступных ресурсов. В некоторых микросервисах может быть всего по одной функции, в других – по много функций.
Существует три типа MOA: синхронные, асинхронные и гибридные.
На рисунке 2 показано синхронное MOA. Межсервисная коммуникация осуществляется по принципу запрос-отклик, типичному для HTTP-взаимодействий в вебе.
Рисунок 2: Микросервисно-ориентированное приложение основано на синхронной межсервисной коммуникации
Каждый микросервис сегментируется за HTTP-сервером, что открывает доступ к логике этого микросервиса. Конкретному микросервису известна только его собственная зона ответственности; также ему могут быть известен интерфейс для взаимодействия с другим микросервисом, но внутреннюю логику другого сервиса он видеть не может. Кроме того, микросервису известно только о его собственных данных. Хранилища данных других микросервисов ему не известны и для него не доступны. «Угнать» данные другого микросервиса, напрямую обратившись за ними в хранилище данных, невозможно. Единственный способ получить данные из микросервиса и передать в него новые данные – взаимодействовать с ним через публичный интерфейс.
Среди достоинств синхронных MOA – их независимость. Например, показанный выше микросервис Customers может обновиться в любой момент, когда ему это удобно. Нет никаких внешних зависимостей, которые требовалось бы к этому приспосабливать. Пока микросервис не меняет своего публичного интерфейса, а также структуру тех данных, которые он предполагает потреблять из запроса и возвращать в качестве отклика, риск сломать всю архитектуру MOA минимален.
По самой своей природе архитектура MOA такова, что сама поддерживает собственную структурную целостность. Поскольку сам микросервис несет собственные данные, его работа не затрагивает хранилищ данных других сервисов. Поскольку микросервис представлен в виде множества HTTP URL и связанных с ними структур данных, построенных по принципу запрос-отклик, границы интерфейса у микросервиса четко определены.
Синхронные MOA становятся все популярнее. Интерфейсы многих синхронных MOA основаны на парадигме REST, этот стиль в ходу с 2000 года. Но, при всей популярности, у MOA есть недостаток: малая скорость работы. Потребители синхронного микросервиса никогда не будут работать быстрее, чем этому микросервису удается справляться с запросами и откликами. Это может быть серьезной помехой, особенно при работе с микросервисами, чьи процессы требуют много времени на выполнение. Пример – сложный аналитический сервис, потребляющий и обрабатывающий терабайты данных. Немногие клиенты согласятся сидеть и ждать несколько минут кряду, пока сервис завершит работу. Было бы удобнее сообщить микросервису, какую работу нужно выполнить, а затем получить уведомление о готовности результатов.
В подобных ситуациях удобнее применять асинхронный подход к проектированию микросервисов.
На рисунке 3 ниже показана асинхронная реализация MOA, где межсервисная коммуникация обеспечивается в виде обмена сообщениями между сторонами, вовлеченными в работу. Как правило, такое взаимодействие называется «выстрелил и забыл».
Рисунок 3: Асинхронная архитектура MOA основана на обмене сообщениями
В асинхронной архитектуре MOA сообщения могут генерироваться произвольно, либо в ответ на заданное событие. Например, когда (как показано на рисунке выше) в микросервисе Orders создается заказ, этот сервис может опубликовать событие
Достоинство асинхронного подхода к проектированию микросервисно-ориентированных приложений в том, что в такой системе не бывает узких мест и, следовательно, она крайне эффективна. Недостаток в том, что такую систему очень сложно создать, а затем управлять ею.
Для крупномасштабных асинхронных систем, таких как Uber, в порядке вещей обрабатывать тысячи сообщений в секунду. Отладка такой системы сложна, поскольку у ее рабочих процессов нет четко прослеживаемых траекторий. Речь не идет о том, что сначала выполняется одно действие, а затем другое; логика срабатывает в зависимости от полученного сообщения, то есть, что угодно может произойти когда угодно.
Об этом следует помнить, прорабатывая стратегии тестирования. Например, непрактична асинхронная система, производительность которой зависит от времени запроса и отклика, так как однозначного соответствия «один запрос-один отклик» не будет никогда.
Сбалансированный подход к реализации микросервисно-ориентированного приложения – гибридный. Сервисы одновременно являются и синхронными, то есть, между ними поддерживается прямая коммуникация запрос-отклик, и в то же время асинхронной; то есть, часть сообщений генерируется вместе с синхронным обменом сообщениями или в результате такого обмена.
На рисунке 4 проиллюстрированы паттерны коммуникации, используемые при гибридном подходе к микросервисно-ориентированным приложениям. Микросервис Customers представлен и в виде URL, связанного с HTTP-сервером и в виде очереди в брокере сообщений.
Рисунок 4: Гибридный подход к проектированию микросервисно-ориентированных приложений, в котором используются как синхронные, так и асинхронные варианты коммуникации между сервисами и потребителями.
К микросервису можно добавить клиента, воспользовавшись стандартным взаимодействием типа запрос-отклик по HTTP. Происходит получение и обработка запроса, затем генерируется отклик. Однако прежде, чем будет сгенерирован отклик, сообщение с информацией о новом клиенте публикуется в сопутствующей очереди сообщений, так, чтобы его могли потреблять другие заинтересованные сервисы. Информация, отправляемая в очередь сообщений, может быть той же, что сгенерирована в HTTP-отклике, либо немного иной, на усмотрение микросервиса. Все зависит от того, как именно спроектирован микросервис, а также от соглашений об уровне качества обслуживания, которые должны выполняться данным микросервисом.
GraphQL – это технология API, поддерживающая одновременно синхронную и асинхронную коммуникацию между потребителем и сервисом. Подробнее об этой технике и подписках GraphQL рассказано здесь.
Важнейшая черта гибридного подхода заключается в том, что в нем сочетаются достоинства обоих других подходов. Но есть и сопутствующие недостатки, в частности, потенциальные узкие места, снижающие производительность, и дополнительная сложность, связанная с необходимостью поддержки двух принципиально разных типов интерфейсов, используемых микросервисом «на вход» и «на выход».
Что касается тестирования таких приложений, в первую очередь необходимо помнить, что весьма редко случается, чтобы один микросервис совместно использовался несколькими MOA. Как правило, микросервис – один из многих в конкретной предметной области в рамках приложения. Достоинство микросервисно-ориентированного подхода к проектированию архитектуры приложения – ускоренная передача кода в продакшен и отдача от него. Так, в Netflix выполняется более 4 000 развертываний в день.) Такая скорость релизов просто невозможна в среде традиционных монолитных приложений.
Также необходимо помнить, что тестирование MOA должно происходить как на микроуровне, так и на макроуровне.
На микроуровне каждый сервис должен быть досконально протестирован в пределах своей зоны ответственности. Согласно некоторым трактовкам, границей микросервиса считается функция.
Целесообразно уделить особое внимание функции внутри микросервиса, учитывая нарастающую популярность бессерверной парадигмы, согласно которой микросервис нужно представлять только как единственную функцию. Но многие практикующие тестировщики при работе с микросервисами склонны ограничиваться модульными тестами. Тогда как модульный тест нормально покрывает единственную функцию, работающую в очень ограниченных тестовых условиях, микросервис рассчитан на обслуживание аудитории в рамках всего Веба. Поэтому условия тестирования должны быть экстремальными.
Например, в рамках хорошего теста на микроуровне можно одновременно прогнать сто тысяч экземпляров микросервиса и посмотреть, как они поведут себя в таком масштабе. Недостаточно за раз проверять всего одну функцию, применяя к ней единственный модульный тест. Необходимо прогонять такие тесты на тысячах экземпляров функции одновременно, с учетом показателей хостинговой среды, в которой придется работать.
Наряду с тестированием функциональности микросервиса, также необходимо позаботиться о тестировании единицы развертывания, в рамках которой происходит релиз микросервиса. Как правило, микросервисы развертываются в виде контейнеров в рамках той или иной технологии оркестрации, например, Kubernetes или Docker Swarm. Важный аспект оркестрации контейнеров – обеспечение долговременной устойчивости микросервисов.
Считается, что микросервисы могут отваливаться по целому ряду причин, как из-за отказа хоста, так и из-за ошибок самого микросервиса. Вполне нормально, что в ходе одновременной эксплуатации тысяч контейнеров возникают какие-то текущие неисправности. Важность тестирования в том, что правильный выход микросервиса из игры не менее важен, чем его корректное функционирование. Тесты на микроуровне гарантируют, что микросервис будет аккуратно оживать и аккуратно умирать, при любом масштабе системы.
Тесты также должны гарантировать, что все существенные события, происходящие в микросервисе, как следует логируются – и, что не менее важно, эти записи логов можно понять. В мире микросервисов записи логов очень важны, в особенности это касается асинхронных MOA, где нет последовательного выполнения поведений. Зачастую только данные логов помогут вам осмыслить, что же творится в приложении.
Независимо от того, является ли MOA синхронным, асинхронным или гибридным, ему показан режим тщательного тестирования. При тестировании микросервисов на макроуровне необходимо убедиться, что два аспекта не вызывают нареканий: межсервисная коммуникация и процессы развертывания.
Микросервисы по определению независимы друг от друга. Они определяют, что им делать, исходя из получаемой ими информации, поэтому точность межсервисной коммуникации критически важна для целостной эксплуатации микросервисно-ориентированного приложения.
Обеспечение межсервисной коммуникации означает организацию того, чтобы правильная информация приходила откуда нужно и уходила куда нужно. Тесты должны позволять отслеживать, как происходит обмен сообщениями, и как они обрабатываются. Это верно как для коммуникации по HTTP в режиме «запрос-отклик», так и для обмена асинхронными сообщениями, распространяемыми через брокер сообщений. Тестирование должно помочь убедиться, что поддерживаются форматы сообщений, обеспечивающие положительный путь работы системы, а неверно отформатированные сообщения отклоняются, причем с достаточными объяснениями (а не просто с ошибкой «плохое сообщение»).
Здраво организованные процессы непрерывной интеграции и непрерывного развертывания (CI/CD) важны в любой парадигме разработки ПО, но, когда речь заходит о микросервисных приложениях, эффективный, точный и быстрый процесс CI/CD критически важен. MOA могут проходить ревизию в темпе сотен обновлений ежедневно, поэтому один микросервис, сборка которого запаздывает, может стать узким местом и затормозить весь процесс релиза.
Наилучший способ от этого перестраховаться – дать тестированию конвейера CI/CD тот же приоритет, что и любому другому высокоуровневому режиму тестирования. Выявление и исправление таких проблем как замедленная сборка микросервисов из-за артефактов в коде, медленное предоставление сред выполнения, в которых хостятся микросервисы, а также медленное поднятие уже развернутых микросервисов – необходимые предпосылки для обеспечения работоспособности конвейера CI/CD. Даже если микросервис сложен как шаттл, от него будет мало пользы, если вы не сможете быстро развертывать и пускать в ход операционные единицы.
Микросервисы – это настоящий джокер. Микросервисно-ориентированные приложения обеспечивают гибкость и скорость, необходимые для вывода новых фич в онлайн практически с молниеносной скоростью. Большие предприятия, занятые поддержкой миллионов пользователей, усвоили это уже сегодня, но день ото дня все больше компаний берут на вооружение этот архитектурный стиль, когда их приложения достигают масштабов, сравнимых с размерами всего веба.
Хотя многие компании всерьез пытаются внедрить дух микросервисно-ориентированной архитектуры в свои процессы разработки, для тестирования этих MOA зачастую применяются практики, исходно предназначенные для монолитных приложений. Это недальновидный подход.
Напротив, компании должны осваивать современные приемы тестирования, рассчитанные на обеспечение независимости микросервисов и динамичности тех приложений, в которых эти микросервисы используются. Когда командам разработчиков и тестировщикам удается синхронизированно подходить к воплощению принципов, лежащих в основе проектирования микросервисных приложений, вся компания в гораздо большей степени может опираться на сильные стороны микросервисов.
Продолжая проработку темы микросервисов, мы решили предложить вам перевод статьи о тестировании MOA (микросервисно-ориентированных приложений). В последнее время мы уже обращались к теме тестирования, но в случае микросервисов обычного модульного тестирования недостаточно, необходимо также учитывать аспекты, связанные с CI/CD и другие вещи, о которых пойдет речь в этой статье.
Микросервисы – новый стиль программной архитектуры, применяемый при создании распределенных систем и все шире внедряемый в компаниях, работающих в Вебе, в том числе, в крупнейших из них. Так, Netflix и Amazon взяли на вооружение микросервисную архитектуру, чтобы значительно ускорить релиз программных продуктов. При этом более древние монолитные системы не могут масштабироваться со скоростью, которая отвечала бы современным вызовам.
Но та польза, которую может принести микросервисная архитектура, хороша лишь настолько, насколько полно удается протестировать практические методы для ее поддержки. При проектировании тестов для проверки микросервисных систем, где темп релизов можно назвать молниеносным, требуется придерживаться новаторского мышления, как на микро- так и на макроуровне.
Далее мы исследуем три типа микросервисно-ориентированных приложений, обсудим некоторые вызовы, с которыми придется справиться, чтобы наладить их тестирование, а также поговорим о том, как пишутся тесты, позволяющие наиболее полно оценить достоинства микросервисов.
Кратко о микросервисах
Прежде чем переходить к некоторым тонкостям проектирования тестов для микросервисов, давайте вспомним, что представляют собой микросервисы. Микросервис – это детально выделенный программный компонент, которому дано четкое семантическое определение; при этом микросервис несет собственный набор данных и не зависит от других структур данных и источников данных. У каждого микросервиса свой цикл развертывания, поэтому после того, как в микросервис были внесены изменения, можно выполнить его релиз, не прерывая работы никаких других микросервисов в области действия того приложения, с которым работает этот микросервис.
Преимущества микросервисных приложений над монолитными
Также давайте рассмотрим, чем микросервис не является. На следующем рисунке показан пример типичного монолитного приложения.
Рис. 1: В архитектурах монолитных приложений обычно наблюдается сильная связанность компонентов
Компоненты Customers (Клиенты), Products (Товары), Orders (Заказы) и (Комментарии) можно трактовать как множества классов, написанных на объектно-ориентированном языке, например, на Java или C#, где customers – это массив объектов, соответствующих клиентам, products – массив объектов, соответствующих товарам и т.д. Объект customer может воспользоваться как объектом customer, так и объектом product. Либо в силу того, что все компоненты монолитного приложения обращаются к одной и той же базе данных, объект order вполне может обратиться напрямую к базе данных и получить всю информацию о товаре и заказе, которая нужна для выполнения поставленных задач. Прямой доступ к базе данных не только возможен – притом, насколько он противоречит духу объектно-ориентированного программирования на основе объектно-реляционного отображения ORM — но и активно используется, особенно в тех сферах, где существует высокий спрос на готовые фичи к условленному сроку, не важно, какой ценой.
В результате получается сильно связанная, порой хрупкая система, где релиз новой версии приложения требует значительной координации между всеми командами разработчиков, участвующими в развитии этой системы. Из-за сильной связанности весь релизный цикл не может идти быстрее, чем самая медленная работа по пересмотру какой-нибудь зависимости. Иными словами, если приложение пора обновлять и вносить в него одновременно новую фичу по клиентам и новую фичу по товарам, то релиз не состоится, пока обе эти фичи не будут готовы. Если на внесение обновления по товарам требуется один день, а на внесение изменения по клиентам требуется три недели, то релиз состоится не ранее чем через три недели. Причем, что еще сильнее усугубляет ситуацию, при релизе может потребоваться не только скоординировать новый код, касающийся клиентов и товаров, но и внести изменения в схему базы данных, и этим также придется управлять в процессе релиза.
Релиз изменений для базы данных – очень деликатное предприятие. При изменении структуры базы данных всегда существует риск непредусмотренных побочных эффектов. Помните, что, если любой компонент может обращаться к базе данных, то он и будет к ней обращаться, причем, даже производить над данными такие операции, которые не входят в зону ответственности данного компонента. Такие «манипуляции вне зоны ответственности» могут приводить к сбоям других компонентов, и подобные сбои могут оставаться незамеченными, пока не произойдет катастрофа. Иногда потенциальная опасность может обнаружиться в коде вплоть до выхода этого кода в продакшен. К сожалению, такое происходит постоянно.
Некоторые компании готовы мириться с медленными релизными циклами, какие характерны для работы с монолитными приложениями. Но, если речь идет о компании, обеспечивающей поддержку сотен тысяч пользователей, и должна при этом поддерживать в работоспособном виде тысячи компонентов, то темп «не быстрее релиза самого запаздывающего компонента» неприемлем.
Микросервисно-ориентированные приложения
В микросервисно-ориентированном приложении (MOA) каждый функциональный компонент разлагается на минимально возможные единицы, обладающие выраженным функционалом (в разумных пределах). В таком случае каждый такой функциональный компонент организуется в виде микросервиса. В каждой компании есть свой багаж унаследованного кода, а также корпоративная культура, которой следует придерживаться, поэтому невозможно расписать «как по нотам», как именно должна осуществляться декомпозиция.
Когда возникает необходимость декомпозировать монолитное приложение в MOA, главное помнить, что лучшее – враг хорошего. Убедитесь, что каждый из микросервисов структурирован согласно его семантическому определению, что для микросервиса предусмотрены собственные данные, а также собственный релизный цикл. Глубина реализации зависит от того, что может себе позволить компания, исходя из имеющегося времени, опыта сотрудников и доступных ресурсов. В некоторых микросервисах может быть всего по одной функции, в других – по много функций.
Существует три типа MOA: синхронные, асинхронные и гибридные.
Синхронные микросервисно-ориентированные приложения
На рисунке 2 показано синхронное MOA. Межсервисная коммуникация осуществляется по принципу запрос-отклик, типичному для HTTP-взаимодействий в вебе.
Рисунок 2: Микросервисно-ориентированное приложение основано на синхронной межсервисной коммуникации
Каждый микросервис сегментируется за HTTP-сервером, что открывает доступ к логике этого микросервиса. Конкретному микросервису известна только его собственная зона ответственности; также ему могут быть известен интерфейс для взаимодействия с другим микросервисом, но внутреннюю логику другого сервиса он видеть не может. Кроме того, микросервису известно только о его собственных данных. Хранилища данных других микросервисов ему не известны и для него не доступны. «Угнать» данные другого микросервиса, напрямую обратившись за ними в хранилище данных, невозможно. Единственный способ получить данные из микросервиса и передать в него новые данные – взаимодействовать с ним через публичный интерфейс.
Среди достоинств синхронных MOA – их независимость. Например, показанный выше микросервис Customers может обновиться в любой момент, когда ему это удобно. Нет никаких внешних зависимостей, которые требовалось бы к этому приспосабливать. Пока микросервис не меняет своего публичного интерфейса, а также структуру тех данных, которые он предполагает потреблять из запроса и возвращать в качестве отклика, риск сломать всю архитектуру MOA минимален.
По самой своей природе архитектура MOA такова, что сама поддерживает собственную структурную целостность. Поскольку сам микросервис несет собственные данные, его работа не затрагивает хранилищ данных других сервисов. Поскольку микросервис представлен в виде множества HTTP URL и связанных с ними структур данных, построенных по принципу запрос-отклик, границы интерфейса у микросервиса четко определены.
Синхронные MOA становятся все популярнее. Интерфейсы многих синхронных MOA основаны на парадигме REST, этот стиль в ходу с 2000 года. Но, при всей популярности, у MOA есть недостаток: малая скорость работы. Потребители синхронного микросервиса никогда не будут работать быстрее, чем этому микросервису удается справляться с запросами и откликами. Это может быть серьезной помехой, особенно при работе с микросервисами, чьи процессы требуют много времени на выполнение. Пример – сложный аналитический сервис, потребляющий и обрабатывающий терабайты данных. Немногие клиенты согласятся сидеть и ждать несколько минут кряду, пока сервис завершит работу. Было бы удобнее сообщить микросервису, какую работу нужно выполнить, а затем получить уведомление о готовности результатов.
В подобных ситуациях удобнее применять асинхронный подход к проектированию микросервисов.
Асинхронные микросервисно-ориентированные приложения
На рисунке 3 ниже показана асинхронная реализация MOA, где межсервисная коммуникация обеспечивается в виде обмена сообщениями между сторонами, вовлеченными в работу. Как правило, такое взаимодействие называется «выстрелил и забыл».
Рисунок 3: Асинхронная архитектура MOA основана на обмене сообщениями
В асинхронной архитектуре MOA сообщения могут генерироваться произвольно, либо в ответ на заданное событие. Например, когда (как показано на рисунке выше) в микросервисе Orders создается заказ, этот сервис может опубликовать событие
orders_created
и поместить его в сопутствующую очередь сообщений, которую слушает другой микросервис, биллинговый (на рисунке не показан) и принимает входящие сообщения. Биллинговый микросервис подхватывает сообщение с информацией о заказе и обрабатывает его таким образом, который релевантен с точки зрения биллинга.Достоинство асинхронного подхода к проектированию микросервисно-ориентированных приложений в том, что в такой системе не бывает узких мест и, следовательно, она крайне эффективна. Недостаток в том, что такую систему очень сложно создать, а затем управлять ею.
Для крупномасштабных асинхронных систем, таких как Uber, в порядке вещей обрабатывать тысячи сообщений в секунду. Отладка такой системы сложна, поскольку у ее рабочих процессов нет четко прослеживаемых траекторий. Речь не идет о том, что сначала выполняется одно действие, а затем другое; логика срабатывает в зависимости от полученного сообщения, то есть, что угодно может произойти когда угодно.
Об этом следует помнить, прорабатывая стратегии тестирования. Например, непрактична асинхронная система, производительность которой зависит от времени запроса и отклика, так как однозначного соответствия «один запрос-один отклик» не будет никогда.
Гибридные микросервисно-ориентированные приложения
Сбалансированный подход к реализации микросервисно-ориентированного приложения – гибридный. Сервисы одновременно являются и синхронными, то есть, между ними поддерживается прямая коммуникация запрос-отклик, и в то же время асинхронной; то есть, часть сообщений генерируется вместе с синхронным обменом сообщениями или в результате такого обмена.
На рисунке 4 проиллюстрированы паттерны коммуникации, используемые при гибридном подходе к микросервисно-ориентированным приложениям. Микросервис Customers представлен и в виде URL, связанного с HTTP-сервером и в виде очереди в брокере сообщений.
Рисунок 4: Гибридный подход к проектированию микросервисно-ориентированных приложений, в котором используются как синхронные, так и асинхронные варианты коммуникации между сервисами и потребителями.
К микросервису можно добавить клиента, воспользовавшись стандартным взаимодействием типа запрос-отклик по HTTP. Происходит получение и обработка запроса, затем генерируется отклик. Однако прежде, чем будет сгенерирован отклик, сообщение с информацией о новом клиенте публикуется в сопутствующей очереди сообщений, так, чтобы его могли потреблять другие заинтересованные сервисы. Информация, отправляемая в очередь сообщений, может быть той же, что сгенерирована в HTTP-отклике, либо немного иной, на усмотрение микросервиса. Все зависит от того, как именно спроектирован микросервис, а также от соглашений об уровне качества обслуживания, которые должны выполняться данным микросервисом.
GraphQL – это технология API, поддерживающая одновременно синхронную и асинхронную коммуникацию между потребителем и сервисом. Подробнее об этой технике и подписках GraphQL рассказано здесь.
Важнейшая черта гибридного подхода заключается в том, что в нем сочетаются достоинства обоих других подходов. Но есть и сопутствующие недостатки, в частности, потенциальные узкие места, снижающие производительность, и дополнительная сложность, связанная с необходимостью поддержки двух принципиально разных типов интерфейсов, используемых микросервисом «на вход» и «на выход».
Какие сложности возникают при проектировании тестов для микросервисов
Что касается тестирования таких приложений, в первую очередь необходимо помнить, что весьма редко случается, чтобы один микросервис совместно использовался несколькими MOA. Как правило, микросервис – один из многих в конкретной предметной области в рамках приложения. Достоинство микросервисно-ориентированного подхода к проектированию архитектуры приложения – ускоренная передача кода в продакшен и отдача от него. Так, в Netflix выполняется более 4 000 развертываний в день.) Такая скорость релизов просто невозможна в среде традиционных монолитных приложений.
Также необходимо помнить, что тестирование MOA должно происходить как на микроуровне, так и на макроуровне.
Тестирование на микроуровне
На микроуровне каждый сервис должен быть досконально протестирован в пределах своей зоны ответственности. Согласно некоторым трактовкам, границей микросервиса считается функция.
Выйти за рамки традиционного модульного тестирования
Целесообразно уделить особое внимание функции внутри микросервиса, учитывая нарастающую популярность бессерверной парадигмы, согласно которой микросервис нужно представлять только как единственную функцию. Но многие практикующие тестировщики при работе с микросервисами склонны ограничиваться модульными тестами. Тогда как модульный тест нормально покрывает единственную функцию, работающую в очень ограниченных тестовых условиях, микросервис рассчитан на обслуживание аудитории в рамках всего Веба. Поэтому условия тестирования должны быть экстремальными.
Например, в рамках хорошего теста на микроуровне можно одновременно прогнать сто тысяч экземпляров микросервиса и посмотреть, как они поведут себя в таком масштабе. Недостаточно за раз проверять всего одну функцию, применяя к ней единственный модульный тест. Необходимо прогонять такие тесты на тысячах экземпляров функции одновременно, с учетом показателей хостинговой среды, в которой придется работать.
Тестируйте единицу развертывания
Наряду с тестированием функциональности микросервиса, также необходимо позаботиться о тестировании единицы развертывания, в рамках которой происходит релиз микросервиса. Как правило, микросервисы развертываются в виде контейнеров в рамках той или иной технологии оркестрации, например, Kubernetes или Docker Swarm. Важный аспект оркестрации контейнеров – обеспечение долговременной устойчивости микросервисов.
Считается, что микросервисы могут отваливаться по целому ряду причин, как из-за отказа хоста, так и из-за ошибок самого микросервиса. Вполне нормально, что в ходе одновременной эксплуатации тысяч контейнеров возникают какие-то текущие неисправности. Важность тестирования в том, что правильный выход микросервиса из игры не менее важен, чем его корректное функционирование. Тесты на микроуровне гарантируют, что микросервис будет аккуратно оживать и аккуратно умирать, при любом масштабе системы.
Убедитесь, что абсолютно все происходящее у вас логируется
Тесты также должны гарантировать, что все существенные события, происходящие в микросервисе, как следует логируются – и, что не менее важно, эти записи логов можно понять. В мире микросервисов записи логов очень важны, в особенности это касается асинхронных MOA, где нет последовательного выполнения поведений. Зачастую только данные логов помогут вам осмыслить, что же творится в приложении.
Тестирование на макроуровне
Независимо от того, является ли MOA синхронным, асинхронным или гибридным, ему показан режим тщательного тестирования. При тестировании микросервисов на макроуровне необходимо убедиться, что два аспекта не вызывают нареканий: межсервисная коммуникация и процессы развертывания.
Обеспечение разумной межсервисной коммуникации
Микросервисы по определению независимы друг от друга. Они определяют, что им делать, исходя из получаемой ими информации, поэтому точность межсервисной коммуникации критически важна для целостной эксплуатации микросервисно-ориентированного приложения.
Обеспечение межсервисной коммуникации означает организацию того, чтобы правильная информация приходила откуда нужно и уходила куда нужно. Тесты должны позволять отслеживать, как происходит обмен сообщениями, и как они обрабатываются. Это верно как для коммуникации по HTTP в режиме «запрос-отклик», так и для обмена асинхронными сообщениями, распространяемыми через брокер сообщений. Тестирование должно помочь убедиться, что поддерживаются форматы сообщений, обеспечивающие положительный путь работы системы, а неверно отформатированные сообщения отклоняются, причем с достаточными объяснениями (а не просто с ошибкой «плохое сообщение»).
Тестирование непрерывной интеграции и непрерывного развертывания
Здраво организованные процессы непрерывной интеграции и непрерывного развертывания (CI/CD) важны в любой парадигме разработки ПО, но, когда речь заходит о микросервисных приложениях, эффективный, точный и быстрый процесс CI/CD критически важен. MOA могут проходить ревизию в темпе сотен обновлений ежедневно, поэтому один микросервис, сборка которого запаздывает, может стать узким местом и затормозить весь процесс релиза.
Наилучший способ от этого перестраховаться – дать тестированию конвейера CI/CD тот же приоритет, что и любому другому высокоуровневому режиму тестирования. Выявление и исправление таких проблем как замедленная сборка микросервисов из-за артефактов в коде, медленное предоставление сред выполнения, в которых хостятся микросервисы, а также медленное поднятие уже развернутых микросервисов – необходимые предпосылки для обеспечения работоспособности конвейера CI/CD. Даже если микросервис сложен как шаттл, от него будет мало пользы, если вы не сможете быстро развертывать и пускать в ход операционные единицы.
Заключение
Микросервисы – это настоящий джокер. Микросервисно-ориентированные приложения обеспечивают гибкость и скорость, необходимые для вывода новых фич в онлайн практически с молниеносной скоростью. Большие предприятия, занятые поддержкой миллионов пользователей, усвоили это уже сегодня, но день ото дня все больше компаний берут на вооружение этот архитектурный стиль, когда их приложения достигают масштабов, сравнимых с размерами всего веба.
Хотя многие компании всерьез пытаются внедрить дух микросервисно-ориентированной архитектуры в свои процессы разработки, для тестирования этих MOA зачастую применяются практики, исходно предназначенные для монолитных приложений. Это недальновидный подход.
Напротив, компании должны осваивать современные приемы тестирования, рассчитанные на обеспечение независимости микросервисов и динамичности тех приложений, в которых эти микросервисы используются. Когда командам разработчиков и тестировщикам удается синхронизированно подходить к воплощению принципов, лежащих в основе проектирования микросервисных приложений, вся компания в гораздо большей степени может опираться на сильные стороны микросервисов.
theRavel
Зашел посмотреть на новые идеи как сочетать E2E/Integration тестирование с Continuous Deployment отдельных микросервисов и т.п., но…