Десять антишаблонов и ловушек микросервисов

Микросервисы Go

Электронная книга O'Reilly «Анти-шаблоны и ловушки микросервисов» описывает десять наиболее распространенных анти-шаблонов и ловушек при реализации проектов микросервисов. В этой статье, основанной на этой книге, перечислены эти десять пунктов. Адрес книги:О, Reilly.com/programming…, более полный антипаттерн и подводные камни можно найти в видео автора:oreil.ly/29GVuDG

Антипаттерн Data-Driven Migration — Data-Driven Migration

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

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

Анти-шаблон тайм-аута — Тайм-аут

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

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

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

Решением этой проблемы является установка более длительного таймаута, например, обычное время ответа службы занимает 2с, а максимальное время 5с, тогда таймаут можно поставить 10с. Но такая проблема в том, что если услуга недоступна, всем потребителям приходится ждать 10 с, что очень требовательно к производительности.

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

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

Netflix by Hystrix — это реализация этого шаблона автоматического выключателя с открытым исходным кодом. Кроме того, Akka также включает реализацию прерывателя цепи: класс Akka CircuitBreaker.

Подробную информацию о «Режиме прерывателя цепи» можно найти по адресу:Martin Fowler.com/B Покидая i/C IRC U….

Поделиться анти-паттерном — меня учили делиться

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

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

  1. Общий проект

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

  2. общая библиотека

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

    Кроме того, если эта схема будет принята, необходимо избегать упаковки всего общего кода в пакет jar, такой как common.jar. В противном случае может быть сложно определить, когда интегрировать изменения библиотеки в службу. Лучший подход — разделить общий код на несколько отдельных контекстных библиотек, таких как: security.jar, dateutils.jar, persistence.jar и т. д. Это облегчит определение того, когда следует интегрировать изменения в общую библиотеку.

  3. избыточность

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

  4. консолидация услуг

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

Анти-шаблон отчетов о достижимости — отчеты о достижимости

Каждая служба и соответствующие ей данные в микрослужбах содержатся в отдельном ограниченном контексте, то есть данные изолированы в нескольких базах данных. Следовательно, это также затрудняет сбор различных данных из микросервисов для создания отчетов. Вообще говоря, есть четыре решения этой проблемы. Среди них первые три извлекают данные из различных микросервисов, которые являются упомянутыми здесь антипаттернами и называются «Reach-in Reporting».

  1. Режим извлечения базы данных

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

  2. HTTP-режим извлечения

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

  3. режим пакетного извлечения

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

  4. Асинхронный режим отправки событий

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

Песчинки - Песчинки

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

«Зерновая ловушка» слишком мелко дробит услуги. Одна из причин в том, что разработчики часто отождествляют класс с сервисом. Разумно, это должен быть сервисный компонент (Service component), соответствующий сервису. Компонент службы имеет четкие, краткие роли, обязанности и определенный набор операций. Обычно это реализуется через несколько модулей (класс Java). Если между компонентами и модулями существует отношение один к одному, это не только приведет к слишком тонкой детализации службы, но и станет плохой практикой программирования: реализация службы полностью осуществляется через класс, тогда класс будет быть очень большим и брать на себя слишком много обязанностей, что не способствует тестированию и обслуживанию.

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

Чтобы избежать «зерновой ловушки», можно использовать следующие три теста, чтобы определить, является ли гранулярность службы разумной:

  1. Объем и функциональность службы анализа

    Чтобы уточнить, для чего используется услуга? Какие операции? Как правило, можно увидеть, выполняет ли служба слишком много работы, используя документ или язык для описания объема и функциональности службы. Если в описании используются такие слова, как «и» или «дополнительно», скорее всего, у службы слишком много обязанностей.

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

    • Добавить клиентов
    • Обновить информацию о клиенте
    • Получить информацию о клиенте
    • Уведомить клиентов
    • Запись отзывов клиентов
    • Получите отзывы клиентов

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

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

  2. Анализ транзакций базы данных

    Все традиционные реляционные базы данных предоставляют функцию транзакций ACID для упаковки нескольких операций обновления в единую общую фиксацию, которая либо завершается успешно, либо завершается ошибкой. В микросервисах, поскольку все сервисы являются отдельными приложениями, реализовать ACID сложно.Как правило, можно реализовать БАЗОВЫЕ транзакции (базовая доступность, мягкое состояние, консистентность в конечном итоге). Но неизбежно, что все еще будут некоторые сценарии, требующие ACID. Поэтому, когда вам постоянно нужно принимать решения и выбирать между транзакциями BASE и ACID, вполне вероятно, что степень детализации службы слишком мала.

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

  3. Оркестрация служб аналитики

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

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

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

Разработчик без причины - Разработчик без причины

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

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

  • Зачем использовать микросервисы?
  • Каковы наиболее важные бизнес-драйверы?
  • Какой пункт в архитектуре самый важный?

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

Следуй за поп-ловушкой - прыгай на подножку

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

преимущество:

  • Простота развертывания. Простота развертывания — большое преимущество микросервисов. Ведь по сравнению с огромным монолитным приложением развертывание небольшого микросервиса с одной ответственностью очень просто и несет гораздо меньший риск. Методы непрерывного развертывания еще больше усиливают это преимущество.
  • Простота тестирования. Единая ответственность и несколько общих зависимостей упрощают тестирование микросервиса. И регрессионное тестирование на основе микросервисов также проще по сравнению с монолитными приложениями.
  • Управление изменениями. Область действия и ограниченный контекст каждой службы упрощают управление изменениями в функциональности службы.
  • Модульность. Микросервисы представляют собой высокомодульный архитектурный стиль. Этот стиль также является выражением гибкого способа быстрого реагирования на изменения. Чем модульнее система, тем проще ее тестировать, развертывать и выпускать изменения. Микросервисная система с разумной степенью детализации сервисов является самой модульной архитектурной формой из всех архитектур.
  • Масштабируемость. Поскольку каждая служба является мелкозернистой службой с единой ответственностью, этот архитектурный стиль является наиболее масштабируемым из всех архитектурных разделений. Очень легко расширить одну или несколько функций, чтобы удовлетворить потребности всей системы. Благодаря контейнерному характеру службы и различным инструментам мониторинга эксплуатации и обслуживания служба также может запускаться и выключаться автоматически.

недостаток:

  • Организационные изменения. Микросервисы требуют организационных изменений на многих уровнях. Команда R&D должна включать в себя различные должности, такие как пользовательский интерфейс, внутренняя разработка, обработка правил, моделирование обработки базы данных и т. д., чтобы небольшая команда могла иметь все технологические стеки для реализации микросервисов. В то же время процесс выпуска программного обеспечения традиционной монолитной и многоуровневой архитектуры приложений также необходимо обновить до автоматизированного и эффективного конвейера развертывания.
  • Производительность: поскольку службы изолированы, инициирование удаленных вызовов служб определенно повлияет на производительность. Оркестровка службы и операционная среда являются основными факторами, влияющими на производительность. Информация о задержке удаленных вызовов и о том, сколько сервисов необходимо передать, — это информация, связанная с производительностью.
  • Надежность: такая же, как производительность. Чем больше удаленных обращений к службе, тем выше вероятность сбоя и ниже общая надежность.
  • DevOps: микросервисная архитектура предоставляет сотни сервисов. Очень непрактично управлять таким количеством сервисов вручную. Это создает серьезные проблемы для автоматизированной эксплуатации и обслуживания, развертывания и совместной работы. Для этого нужно полагаться на множество операционных инструментов и методов, и это очень сложная работа. В настоящее время существует почти 12 типов операционных инструментов (средства мониторинга, регистрации служб, средства обнаружения, средства развертывания и т. д.) и фреймворков, используемых в микросервисных архитектурах, каждый из которых содержит множество конкретных инструментов и продуктов на выбор. Выбор этих инструментов и сред обычно требует почти месяцев исследований, тестирования и анализа компромиссов, чтобы сделать выбор наиболее подходящей технологии.

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

  • Каковы цели бизнеса и технологий?
  • Для чего используются микросервисы?
  • Каковы текущие и прогнозируемые болевые точки?
  • Каковы наиболее важные технические характеристики приложения? (производительность, простота развертывания, простота тестирования, масштабируемость)

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

Помимо микросервисов, на выбор есть 7 других часто используемых архитектур:

  • на основе услуг
  • Сервис-ориентированный
  • Многоуровневая архитектура
  • Архитектура микроядра (микроядро)
  • Космический
  • Архитектура, управляемая событиями
  • Конвейерная архитектура (конвейер)

Ловушка статического контракта - Статический контракт

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

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

  • Добавить номер версии в заголовок

    Как показано на рисунке, этот метод добавляет информацию о версии в заголовок протокола удаленного доступа. И если удаленный протокол использует REST, вы также можете использовать mime-тип поставщика (vnd), чтобы указать номер версии контракта. следующим образом:

      POST /trade/buy
      Accept: application/vnd.svc.trade.v2+json
    

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

    При использовании очереди сообщений вы можете указать номер версии в разделе «Свойства». Пример JMS выглядит следующим образом:

      String msg = createJSON("acct","12345","sedol","2046251","shares","1000");
      jsmContext.createProducer()
          .setProperty("version",2)
          .send(queue,msg);
    
  • Добавьте номер версии к самому контракту

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

Мы там, ловушка - мы еще там

В микросервисной архитектуре каждая служба является независимой сущностью, что означает, что вся связь между клиентом или уровнем API и службой является удаленным вызовом. Если вы не понимаете, сколько времени уходит на эти удаленные звонки, вы попадаете в ловушку «Мы уже там?». Разумный подход требует проверки таких показателей, как средняя задержка удаленного доступа, задержка с длинным хвостом (задержка запроса выше 95%, 99% и 99%). И часто даже при хорошей средней задержке плохая задержка длинного хвоста может быть очень разрушительной.

Тестирование в производственной или квазипроизводственной среде помогает понять реальную производительность приложения. Например, бизнес-запрос должен вызвать четыре службы. Если предположить, что задержка вызова службы составляет 100 миллисекунд, с учетом задержки самого бизнес-запроса для выполнения бизнес-запроса требуется в общей сложности 500 миллисекунд задержки. Это отличается от вывода, сделанного только из кода.

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

Как показано на рисунке, производительность протокола AMQP является наилучшей. Затем в сочетании с бизнес-сценарием вы можете выбрать REST в качестве протокола связи между клиентом и службой и AMQP в качестве протокола связи между службами для повышения производительности приложения.

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

Подводные камни использования REST - Дайте ему отдохнуть

REST в настоящее время является наиболее часто используемым протоколом связи в микросервисах. Популярные среды разработки, такие как DropWizard и Spring Boot, обеспечивают поддержку REST. Но если вы выберете только REST в качестве протокола и не будете учитывать другие преимущества, такие как очереди сообщений, вы попадете в ловушку «использования REST». В конце концов, требования асинхронной связи, широковещательной рассылки и транзакций мерж-реквестов трудно выполнить в REST.

Стандарты очередей сообщений в настоящее время включают как специфичные для платформы, так и независимые от платформы. Первый включает JMS на платформе Java и MSMQ на платформе C#, а второй — AMQP. Для стандарта сообщений JMS для конкретной платформы он регулирует API, поэтому нет необходимости изменять API при переключении реализаций брокера (ActiveMQ, HornetQ).Однако, поскольку базовые протоколы связи различны, интегрированный клиентский или серверный пакет jar необходимо соответствующим образом изменить. Для независимого от платформы стандарта сообщений он определяет стандарт реализации протокола, но не определяет API. Это позволяет различным платформам взаимодействовать друг с другом, независимо от фактического продукта. Например, клиент, использующий RabbitMQ, может легко обмениваться данными с StormMQ (при условии, что используется тот же протокол). То есть его независимая от платформы природа делает RabbitMQ самой популярной очередью сообщений в микросервисных архитектурах.

  1. асинхронный запрос

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

  2. транслировать

    Широковещательная рассылка сообщений нескольким службам — еще один вариант использования очередей сообщений. Производитель сообщений отправляет сообщение нескольким получателям сообщений, не зная, кто получает сообщение и что с ним делать.

  3. запрос транзакции

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

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