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

  • независимо развиваться (эволюционировать); 

  • независимо тестироваться;

  • независимо развертываться.

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

Независимое развитие

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

Например, если говорить о REST, то есть миллион статей об эволюции API, в которых рассказывается об обновлении API без влияния на клиентов.

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

Проприетарные протоколы остаются на вашей совести. Вероятно, вам придется изобретать велосипед.

Независимое тестирование 

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

Я рекомендую познакомиться со Spring Cloud Contract WireMock и Spring REST Docs, которые позволят вам:

  • писать документацию на основе тестов;

  • создавать заглушки (стабы) для вашего сервиса;

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

Пример использования Spring Cloud Contract:

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = MOCK)
@AutoConfigureMockMvc
@AutoConfigureStubRunner(stubsMode = LOCAL, ids = "com.github.wirtsleg:my-service-stub:+:stubs:8090")
public abstract class AbstractIntegrationTest {
    ...
}

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

Независимое развертывание 

Не все могут себе позволить остановку сервиса при обновлении. Но это неизбежно, если есть циклические зависимости (новому сервису A нужен новый B, новому B нужен новый C, новому C нужен новый A). И я уверен, что ни у кого нет желания координировать свои действия с другими командами для релиза новой версии своего микросервиса. О том, как решить эту проблему, мы говорили ранее в разделе о независимом развитии. Но есть еще один момент: сервис должен уживаться со своей предыдущей версией.

Вы, вероятно, слышали термины "сине-зеленое развертывание" (Blue-Green deployment) и "канареечное развертывание" (Canary deployment). Эти подходы предполагают, что новая и старая версии будут сосуществовать какое-то время. А это влечет за собой использование одной и той же базы данных, распределенного кэша и т.д.

Нам нужно поддерживать совместимость с одной предыдущей версией.

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

  1. Добавить  новое поле в версии 2 и поддерживать оба поля.

  2. Начать использовать только новое поле в версии 3.

  3. Удалить старое поле в версии 4.

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

Аналогичный подход применяется к распределенному кешу и NoSQL-базам данных.

Заключение

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


Материал подготовлен нашим экспертом - Александром Коженковым и опубликован в преддверии старта курса «Microservice Architecture». Если вам интересно узнать подробнее о формате обучения и программе, познакомиться с преподавателем курса — приглашаем на день открытых дверей онлайн. Регистрация здесь.

Комментарии (4)


  1. derikn_mike
    31.08.2021 21:41

    знаю одну такую независимость....

    Имеет очень большую таблицу в одной базе (сервисе контента)

    И имеем статистику к этому контенту в базе 2 (сервисе статистике)

    И вдруг понадобилась сделать join по ним с фильтрами для базы1 и сортировкой по базе2.

    Удачи!!!


    1. BugM
      01.09.2021 02:20
      +3

      Заводим базу 3 в которую льем данные из первых двух. Назовем ее "Аналитическим Хранилищем" чтобы бюджет полегче выбить было.

      Обычные будни энтерпрайза.


      1. derikn_mike
        01.09.2021 15:01

        тоесть вы хотите

        1) совместить абсолютно разные "микросервисные" которые по логике и смыслу работали идеально не зная об друг друга совместить в одну?

        2) И самое главное это производительность , вы хотите в одну базу слить эти две логике , у которых в А - редчайшие обновления , Б(статистика) - невероятно сильная нагрузка и апдейты млн раз в секунду. Вы хотите в вашем подходе теперь чтобы тормозила невиновная А из за этого?


        1. BugM
          01.09.2021 16:58

          Зачем? Все остаётся как есть. Все сервисы и дальше работают со своей базой.

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

          Когда нужен запрос с join делаем его из этой третьей БД. Учитывая ограничения на которые мы подписались при выборе алгоритма заливки данных.