Введение

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

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

Тем не менее при определенных условиях микросервисная архитектура может дать сбой и стать неэффективной.

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

1. Использование монолита в микросервисах

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

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

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

2. “Общительные” микросервисы

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

Рассмотрим некоторые сценарии, которые могут значительно снизить эффективность, способствуя появлению “болтливых” микросервисов.

  1. Частое взаимодействие сервисов. Бывают случаи, когда микросервисы внутри системы отправляют большое количество запросов другим микросервисам для выполнения мелких задач или получения небольших объемов данных. Это может приводить к генерации большого объема сетевого трафика и увеличивать задержку ответа.
  2. “Мелкозернистые” API. Микросервисы предоставляют “мелкозернистые” API, которые требуют множества вызовов для выполнения одного пользовательского запроса или бизнес-транзакции. Каждый вызов может потребовать сериализации, сетевых накладных расходов и даже блокировки операций ввода-вывода, что увеличивает проблемы с производительностью.
  3. Каскад вызовов. Один пользовательский запрос или транзакция инициирует серию вызовов между несколькими микросервисами, при этом каждый сервис полагается на другие для завершения процесса обработки. Такая ситуация может вызвать эффект домино, при котором отказ или задержка одного сервиса распространяется на другие, что приводит к ухудшению работы всей системы.

Вы избежите этой проблемы, если спроектируете архитектуру таким образом, чтобы сервисы были разделены и более масштабируемы. Внедрить очереди сообщений, шины событий и темы событий можно, используя такие сервисы, как Amazon SQS, Amazon SNS и Amazon EventBridge.

Как видите, микросервис Order Acknowledgment взаимодействует с микросервисами Shipment, Inventory и Notification. Однако он не взаимодействует с этими сервисами напрямую. Вместо этого он использует вспомогательные сервисы, такие как SQS и SNS, чтобы разделить поведение, связанное с коммуникацией.

Таким образом, вы можете избежать межсервисного взаимодействия и разделить коммуникацию между сервисами.

3. Распределенный монолит

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

Вот некоторые из ключевых характеристик этого антипаттерна.

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

Рассмотрим в качестве примера эту архитектуру:

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

Опять же, вы можете избавиться от этой проблемы, “развязав” микросервисы с помощью таких сервисов, как SNS и SQS.

4. Чрезмерное внедрение микросервисов

Одно из самых распространенных заблуждений при проектировании архитектур на основе микросервисов  —  разбивать каждую функцию на микросервисы, даже самые простые из них!

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

Ниже перечислены основные характеристики этого антипаттерна.

  1. Излишняя фрагментация. Система делится на значительное количество микросервисов, что приводит к десяткам или даже сотням сервисов. В результате этого каждый микросервис может инкапсулировать лишь небольшую часть функциональности, что приводит к слишком “мелкозернистой” декомпозиции.
  2. Недостаточная согласованность. Отдельным микросервисам не хватает согласованности, то есть они могут не включать логически связанные или последовательные наборы функциональности. Это может привести к разрозненности и фрагментарности бизнес-логики, что затрудняет понимание и управление системой в целом.
  3. Высокая степень связанности. Несмотря на разделение на микросервисы, сервисы могут быть тесно связаны между собой из-за значительного межсервисного взаимодействия и взаимозависимости. Изменения в одном микросервисе могут потребовать изменений во многих других, что увеличивает сложность системы и уровень ее подверженности рискам.

Один из способов решить эту проблему  —  внедрить принципы предметно-ориентированного программирования в микросервисы и создавать микросервисы для конкретных доменов приложения.

5. Нарушение принципа единой ответственности

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

Рассмотрим некоторые сценарии, которые способствуют развитию этого антипаттерна.

  1. Отсутствие осведомленности о принципах проектирования. Разработчики могут не знать или не понимать такие концепции проектирования, как принцип единой ответственности (SRP), или не приоритизируют приложение в кодовой базе.
  2. Неправильное планирование. Неправильное планирование или анализ на ранних этапах разработки программного обеспечения может привести к нечеткому распределению обязанностей между компонентами, что приводит к смешению множества задач в одном компоненте.
  3. Неверное толкование требований. Неправильное понимание или недопонимание требований может привести к внедрению ненужной или неактуальной функциональности в компонент, что нарушает принцип единой ответственности (SRP).

6. Спагетти-архитектура

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

Вот основные характеристики этого антипаттерна.

  1. Отсутствие разделения задач. В такой архитектуре не удается разделить различные задачи и обязанности, что приводит к смешению бизнес-логики, логики представления, логики доступа к данным и других функций внутри одного компонента или модуля.
  2. Сложный поток управления. Поток управления в этой архитектуре сложный и запутанный, с зависимостями и взаимодействиями между компонентами, которые трудно отследить или понять. Это может привести к непредсказуемому поведению и нежелательным последствиям.
  3. Высокая степень связанности. Как мы видели в одном из предыдущих примеров, компоненты или модули в такой архитектуре тесно связаны между собой, что означает их сильную зависимость друг от друга. Изменения в одном компоненте иногда требуют изменений в нескольких других компонентах, и это вызывает “волновой” эффект по всей системе.

7. Несогласованность распределенных данных

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

Вот основные характеристики антипаттерна.

  1. Асинхронные обновления. Обновления данных распространяются асинхронно по всем репликам или узлам распределенной системы. Это может привести к задержкам между выполнением изменения и его отражением на всех копиях данных.
  2. Разделение сети. По непредвиденным причинам могут возникнуть разделения сети или сбои, которые не позволяют обновлениям распространяться по всем репликам или приводят к расхождениям между репликами из-за неполного обновления.
  3. Конфликтующие операции. Одновременные операции с одними и теми же данными на многих узлах могут вызвать конфликты, которые не будут должным образом обработаны, что приведет к несогласованности или повреждению данных.

Чтобы решить подобные проблемы, важно использовать паттерны микросервисов, такие как Saga Pattern, для создания и управления распределенными транзакциями в микросервисах.

8. Прочная связанность

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

Это способствует возникновению многих антипаттернов, таких как:

  • монолитная архитектура;
  • спагетти-архитектура;
  • божественный объект;
  • несогласованность распределенных данных;
  • привязка к поставщику.

9. Отсутствие наблюдаемости

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

Вот основные характеристики антипаттерна.

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

Рассмотрите возможность использования встроенных в облако инструментов, таких как AWS X-Ray, или сторонних платформ, таких как New Relic.

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

10. Игнорирование человеческого фактора

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

Вот основные характеристики антипаттерна.

  1. Переутомление. Члены команды часто вынуждены работать в течение длительного времени, в том числе по вечерам, выходным и праздникам, чтобы выполнить проект в срок или решить непредвиденные проблемы. Это может привести к выгоранию, усталости и снижению производительности.
  2. Нереалистичные ожидания. Сроки и результаты проекта определяются без учета имеющихся у команды ресурсов, навыков и возможностей. Это приводит к появлению нереалистичных планов и оказывает ненужное давление на членов команды, заставляя их работать в условиях сжатых сроков.
  3. Микроменеджмент. Менеджеры или руководители команд используют чрезмерный контроль или микроменеджмент по отношению к членам команды, что резко снижает самостоятельность, креативность и мотивацию.
  4. Отсутствие поддержки. Столкнувшись с проблемами или трудностями на работе, члены команды чувствуют отсутствие поддержки со стороны руководства или коллег. Это может усилить чувство одиночества, повысить градус напряженности и привести к нежеланию продолжать работу.

Выводы

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

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

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

Читайте также:

Читайте нас в Telegram, VK и Дзен


Перевод статьи Lahiru Hewawasam: Top 10 Microservices Anti-Patterns

Предыдущая статьяКак создать Android-приложение чат-бота с генеративным ИИ Google
Следующая статьяРуководство по Git для новичков