Референсная архитектура для микросервисов. Напоминание о том, как много всего нужно не забыть.
В микросервисах, живущих в облаках кеширование может серьезно снизить стоимость решения. Для «не облаков» это не очевидно, но там, где оплата идет за используемые ресурсы (процессор, память, доступ к данным) — выгода просчитывается достаточно просто, главное не забыть о других атрибутах качества, таких как безопасность, степень актуальности данных, производительность и стоимость поддержки самого кеша.
The Adventures of Microservice Episode 1 – The Birth of Microservice
The Adventures of Microservice Episode 2 – The Kickoff Meeting
The Adventures of Microservice Episode 3 (новее пока нет) – In the Data Center
Монолитра!)
Монолитра!)
Cаммари отчета «Engineering Reliable Mobile Applications» от Саши Поломодова:
https://medium.com/@alexanderpolomodov/sre-практики-в-разработке-мобильных-приложений-c9313d915e74
https://medium.com/@alexanderpolomodov/sre-практики-в-разработке-мобильных-приложений-c9313d915e74
Medium
SRE практики в разработке мобильных приложений
Компания Google щедро делится своими практиками на тему построения надежных сервсисов (подробнее в источниках 2, 3, 4), а в прошлом году…
Когда мы говорим о том, какими микросервисы должны быть с концептуальной точки зрения, мы всегда держим в уме и повторяем как мантру: «слабая связанность» и «сильное сцепление». Всегда.
И маршируя по Legacy Street за переход к более гибкой архитектуре, на наших транспарантах будут именно словосочетания «слабая связанность» и «сильное сцепление» :)
Что главное? Возможность внесения изменений и развертывание сервиса без необходимости внесения изменений в любую другую часть системы. Оно же — Low Coupling.
Сильное сцепление (High Cohesion) — я, как кем бы я ни был, хочу, чтобы связанное поведение находилось в одном месте, внутри некой границы, которая имела бы как можно более слабую связь с другими границами.
Вот тут появляется ограниченный контекст (Bounded Context) или иначе — конкретная ответственность, обеспечиваемая четко обозначенными границами.
И если мы хотим перейти от монолита к микро, то мы сначала очень аккуратно выделяем контексты, определяем модель (внутреннюю для контекста и общую, для общения), повышаем модульность системы. Уверены? Выносим модуль в сервис.
И думаем о сервисах в терминах бизнес-возможностей. Сначала «Чем контекст (модуль, сервис) занимается и какие услуги предоставляет?», затем «Что (какие данные, внутренние или из других контекстов) ему нужны?»
И маршируя по Legacy Street за переход к более гибкой архитектуре, на наших транспарантах будут именно словосочетания «слабая связанность» и «сильное сцепление» :)
Что главное? Возможность внесения изменений и развертывание сервиса без необходимости внесения изменений в любую другую часть системы. Оно же — Low Coupling.
Сильное сцепление (High Cohesion) — я, как кем бы я ни был, хочу, чтобы связанное поведение находилось в одном месте, внутри некой границы, которая имела бы как можно более слабую связь с другими границами.
Вот тут появляется ограниченный контекст (Bounded Context) или иначе — конкретная ответственность, обеспечиваемая четко обозначенными границами.
И если мы хотим перейти от монолита к микро, то мы сначала очень аккуратно выделяем контексты, определяем модель (внутреннюю для контекста и общую, для общения), повышаем модульность системы. Уверены? Выносим модуль в сервис.
И думаем о сервисах в терминах бизнес-возможностей. Сначала «Чем контекст (модуль, сервис) занимается и какие услуги предоставляет?», затем «Что (какие данные, внутренние или из других контекстов) ему нужны?»
В последнее время GraphQL стали предрекать смерть от http/2. Ниже мои мысли на этот счет.
Давным-давно сайты были легкими и HTTP/1 отлично гонял свои килобайты по стабильному соединению (один ресурс — одно TCP соединение). Шло время, а вместе с ним увеличивались объемы данных и росло количество запросов. Это порождало большие накладные расходы на дорогие операции открытия TCP. Помню, как мы старались склеивать картинки, лишь бы сократить их количество 🙂
Стало совсем невыносимо и появился HTTP/1.1 с persistent (keep-alive) connections и pipeline. Мы смогли отсылать несколько запросов и ответов в одно соединение и это было реально круто! Вот только один медленный запрос блокировал последующие, а бездействующее соединение все же потребляет какие-никакие ресурсы.
Что сделал GraphQL? Он позволил клиенту запросить все ресурсы за раз. Клиент сам решает, какие данные ему нужны, запрашивает их и получает одним пакетом. Вспомним времена HTTP/1, когда для заполнения одного экрана нужно было сделать несколько реквестов, в каждом ответе по 15 полей из которых нужных — по 2-3 максимум. Знатный оверхед.
В HTTP/2 (к слову — он стал бинарным, что уже сделало его более быстрым) появилась новая фича — request multiplexing. Эта фича позволяет точно так же, как в HTTP/1.1 использовать одно соединение для множества запросов, но только здесь все запросы выполняются параллельно, что исключает блокировку последующих запросов одним медленным.
Фактически в HTTP/2 заложено решение той проблемы, которую решал GraphQL с точки зрения производительности. И если брать скорость как единственный критерий, то GraphQL уже не так и нужен. Но на GraphQL можно посмотреть шире — он сдвинул парадигму в сторону клиентоцентричности.
На уровне обращения к API всегда бэк решал, что отдать фронту. «Хочешь получить имя пользователя? Вот готовый сервис. Возвращающий еще три десятка параметров (за которыми вызовы десятка микросервисов).». И вот с помощью GraphQL наконец фронт может сам решать, какие данные ему нужны и запрашивать только их (а значит будут вызваны только микросервисы, возвращающе конкретные данные). Это фундаментальный сдвиг парадигмы.
Можно долго спорить о надежности GraphQL, но то, что он привнес своего рода клиентоцентричность в процесс разработки — этому можно только порадоваться, так что если его кто и похоронит GraphQL, то не HTTP/2, а другой инструмент, развивающий эту парадигму.
Давным-давно сайты были легкими и HTTP/1 отлично гонял свои килобайты по стабильному соединению (один ресурс — одно TCP соединение). Шло время, а вместе с ним увеличивались объемы данных и росло количество запросов. Это порождало большие накладные расходы на дорогие операции открытия TCP. Помню, как мы старались склеивать картинки, лишь бы сократить их количество 🙂
Стало совсем невыносимо и появился HTTP/1.1 с persistent (keep-alive) connections и pipeline. Мы смогли отсылать несколько запросов и ответов в одно соединение и это было реально круто! Вот только один медленный запрос блокировал последующие, а бездействующее соединение все же потребляет какие-никакие ресурсы.
Что сделал GraphQL? Он позволил клиенту запросить все ресурсы за раз. Клиент сам решает, какие данные ему нужны, запрашивает их и получает одним пакетом. Вспомним времена HTTP/1, когда для заполнения одного экрана нужно было сделать несколько реквестов, в каждом ответе по 15 полей из которых нужных — по 2-3 максимум. Знатный оверхед.
В HTTP/2 (к слову — он стал бинарным, что уже сделало его более быстрым) появилась новая фича — request multiplexing. Эта фича позволяет точно так же, как в HTTP/1.1 использовать одно соединение для множества запросов, но только здесь все запросы выполняются параллельно, что исключает блокировку последующих запросов одним медленным.
Фактически в HTTP/2 заложено решение той проблемы, которую решал GraphQL с точки зрения производительности. И если брать скорость как единственный критерий, то GraphQL уже не так и нужен. Но на GraphQL можно посмотреть шире — он сдвинул парадигму в сторону клиентоцентричности.
На уровне обращения к API всегда бэк решал, что отдать фронту. «Хочешь получить имя пользователя? Вот готовый сервис. Возвращающий еще три десятка параметров (за которыми вызовы десятка микросервисов).». И вот с помощью GraphQL наконец фронт может сам решать, какие данные ему нужны и запрашивать только их (а значит будут вызваны только микросервисы, возвращающе конкретные данные). Это фундаментальный сдвиг парадигмы.
Можно долго спорить о надежности GraphQL, но то, что он привнес своего рода клиентоцентричность в процесс разработки — этому можно только порадоваться, так что если его кто и похоронит GraphQL, то не HTTP/2, а другой инструмент, развивающий эту парадигму.
Легким движением руки ноут с 8GB превращается в микро датацентр (с подробной инструкцией)
В микросервисах нередко I/O (например — работа с базой) операции рассматриваются как часть Unit’а в контексте Unit-тестирования. Таким образом, Unit-тесты начинают больше походить на интеграционные, интеграционные тесты — на тесты на живой системе в проде, а прод-тесты — на мониторинг и исследование. И в целом уже несколько раз приходилось переопределять состав юнита (минимальной, атомарной единицы). Ведь если сервис в 95% случаем обращается к базе и фактически это весь его код, то появлсяется смысл рассматривать вызов базы как часть юнита, даже несмотря на возможные side effect’ы с сетью и тем самым получить больший outcome от тестов за счет снижения стоимости поддержки (отсутствия заглушек и двух наборов тестов), фокусируясь на бизнес-функциях.
У многих разработчиков и архитекторов законно возникает множество вопросов к согласованности данных в микросервисах. Некоторые приходят к паттерну SAGA и вопросов становится еще больше 🙂 Saga из тех паттернов, к которым интуитивно подходит я бы не советовал по двум причинам:
1. Она все-таки сложна в реализации и
2. Нередко затрагивает достаточно важные бизнес-процессы в распределенной, событийной системе
То есть вероятность завалить всё высокая, а исправить быстро (и ладно бы исправить — банально понять в чем дело) получается далеко не всегда.
А ведь больше половины ответов на чаще всего возникающие вопросы содержатся прям вот в том самом документе, который её и породил: https://www.cs.cornell.edu/andru/cs711/2002fa/reading/sagas.pdf
И практически все статьи повторяют в том или ином виде описанное в этой статье (иногда дополняя технологическими особенностями, иногда приводя примеры из конкретных предметных областей). Но я все же считаю, что если уж и изучать что-то сложное, то начинать с первоисточников (если первоисточники не дискридитированы, но тогда и о чтении базирующихся на первоисточнике материалов стоит задуматься), после чего можно переходить к интерпретациям.
Кто еще не читал — must read!
1. Она все-таки сложна в реализации и
2. Нередко затрагивает достаточно важные бизнес-процессы в распределенной, событийной системе
То есть вероятность завалить всё высокая, а исправить быстро (и ладно бы исправить — банально понять в чем дело) получается далеко не всегда.
А ведь больше половины ответов на чаще всего возникающие вопросы содержатся прям вот в том самом документе, который её и породил: https://www.cs.cornell.edu/andru/cs711/2002fa/reading/sagas.pdf
И практически все статьи повторяют в том или ином виде описанное в этой статье (иногда дополняя технологическими особенностями, иногда приводя примеры из конкретных предметных областей). Но я все же считаю, что если уж и изучать что-то сложное, то начинать с первоисточников (если первоисточники не дискридитированы, но тогда и о чтении базирующихся на первоисточнике материалов стоит задуматься), после чего можно переходить к интерпретациям.
Кто еще не читал — must read!
Статья «Размер микросервиса»
Есть много статей о размерах, много о изоляции, но мне пока не встречались статьи (пропустил?), в которых изоляция и размер рассматривались бы в едином контексте. Изложил свои мысли на этот счет как обобщение практического опыта.
Идея простая — изоляция важнее размера.
Есть много статей о размерах, много о изоляции, но мне пока не встречались статьи (пропустил?), в которых изоляция и размер рассматривались бы в едином контексте. Изложил свои мысли на этот счет как обобщение практического опыта.
Идея простая — изоляция важнее размера.
В субботу пройдет TechTrain, «бесплатный небольшой онлайн-фестиваль, объединяющий разработчиков, инженеров и им сочувствующих».
По теме канала будет про тестирование приложений с потоковыми процессами (kafka) от Виктора Гамова (confluent) и об управлении зависимостями в CI/CD от Олега Ненашева(cloudbees).
По теме канала будет про тестирование приложений с потоковыми процессами (kafka) от Виктора Гамова (confluent) и об управлении зависимостями в CI/CD от Олега Ненашева(cloudbees).
Как Spotify мигрировал свои 1200 микросервисов в Google Cloud.
Просто грандиознейшее переселение микросервисов.
— Сформировали команду миграции
— Разделили миграцию на две части: миграция сервисов и миграция данных
— Визуализировали все сервисы, цветами помечая перенесённые (в статье написано зачем)
— Затем разделили стратегию миграции сервисов еще на две: миграция сервисов и миграция пользовательского трафика
— Мигрировали итерациями (1-2 недельными)
— Команда миграции втихую ломала смигрированное, чтобы посмотреть на реакцию разработчиков 😈
По ссылке статья и видео (англ)
Просто грандиознейшее переселение микросервисов.
— Сформировали команду миграции
— Разделили миграцию на две части: миграция сервисов и миграция данных
— Визуализировали все сервисы, цветами помечая перенесённые (в статье написано зачем)
— Затем разделили стратегию миграции сервисов еще на две: миграция сервисов и миграция пользовательского трафика
— Мигрировали итерациями (1-2 недельными)
— Команда миграции втихую ломала смигрированное, чтобы посмотреть на реакцию разработчиков 😈
По ссылке статья и видео (англ)
Внезапно прошла волна репостов статьи «Microservices considered harmful». Ссылка ниже.
Первая мысль из статьи
>>Если у вас спагетти в монолите, то при делении вы получите «spaghetti over HTTP».
Все правильно, только при чем тут микросервисы? Не устаю повторять две вещи:
1. «Выбирайте микросервисы за их преимущества, а не потому, что код монолита ужасен»
2. «Не нарезайте монолит AS IS, проведите моделирование с самого начала, постройте корректную модель предметной области, выделите независимые модули/пакеты внутри монолита и затем выносите микросервисы»
Вторая мысль из статьи
>>Производительность хуже, потому что пакеты бегают по сети.
Сравниваем время обработки сообщений и время на передачу данных. Потеряли на передаче 5 секунд, выиграли на параллельной обработке 10 минут. Так это работает.
>>Масштабируемость хуже, потому что… лучше масштабировать все целиком и появляются новые точки отказа.
Есть великолепные паттерны и инструменты изоляции сбоев. И независимое масштабирование дает космический эффект в облаках, где оплата за использованные ресурсы.
А вот вывод отличный: «keep in mind that almost all technical challenges (code modularity, scalability, single point of failure…) will not be magically solved by using microservices». Все по делу.
———
Микросервисы — не панацея, они сложны, но у них есть существенные преимущества. Эти преимущества не нужны всем, выбор микросервисов должен быть очень прагматичным выбором. Монолит может быть качественно и модульно написан. Микросервисы нужны тогда, когда требуется независимая поставка/независимая замена/независимое развитие/независимое масштабирование/независимый выбор технологий и это действительно дает серьезное конкуретное преимущество. А не ради хайпа 🙂
Первая мысль из статьи
>>Если у вас спагетти в монолите, то при делении вы получите «spaghetti over HTTP».
Все правильно, только при чем тут микросервисы? Не устаю повторять две вещи:
1. «Выбирайте микросервисы за их преимущества, а не потому, что код монолита ужасен»
2. «Не нарезайте монолит AS IS, проведите моделирование с самого начала, постройте корректную модель предметной области, выделите независимые модули/пакеты внутри монолита и затем выносите микросервисы»
Вторая мысль из статьи
>>Производительность хуже, потому что пакеты бегают по сети.
Сравниваем время обработки сообщений и время на передачу данных. Потеряли на передаче 5 секунд, выиграли на параллельной обработке 10 минут. Так это работает.
>>Масштабируемость хуже, потому что… лучше масштабировать все целиком и появляются новые точки отказа.
Есть великолепные паттерны и инструменты изоляции сбоев. И независимое масштабирование дает космический эффект в облаках, где оплата за использованные ресурсы.
А вот вывод отличный: «keep in mind that almost all technical challenges (code modularity, scalability, single point of failure…) will not be magically solved by using microservices». Все по делу.
———
Микросервисы — не панацея, они сложны, но у них есть существенные преимущества. Эти преимущества не нужны всем, выбор микросервисов должен быть очень прагматичным выбором. Монолит может быть качественно и модульно написан. Микросервисы нужны тогда, когда требуется независимая поставка/независимая замена/независимое развитие/независимое масштабирование/независимый выбор технологий и это действительно дает серьезное конкуретное преимущество. А не ради хайпа 🙂
Сегодня на TechLeadConf после моего выступления был вопрос — что делать, если приходится часто менять сервисы (примерно так). Вопрос касался Event Storming, — как часто корректировать модель, стратегический дизайн, как часто проводить периодические шторминги?
Короткий ответ — в зависимости от частоты изменений предметной области.
Высокая динамика изменений? Проводим чаще.
Предметная область относительно стабильна? Проводим реже.
Главное — делать это регулярно. Почему? Потому, что если будем назначать встречи по пересмотру модели, то такие встречи будут происходить после чего? Именно) После появления проблем. Регулярность даёт возможность обнаружить проблемы до того, как они стали реальными. Если же на регулярные встречи чаще приходим с уже случившимися проблемами дизайна — есть смысл начать проводить такие встречи чаще.
Скоро завершу оформление статьи по итогам выступления, а пока пример, когда пришлось склеить два микросервиса в один. Обычные дела)
Для тех, кто любит сестру таланта, основная мысль:
«When adding new functionality we should question whether our architecture still justifies itself. In this case, at the beginning it seemed that a ride and a prebook have a right to live as separate entities, and therefore have separate services and databases. As more requirements and features arrived, it became clear that the separation of services caused cumbersomeness.»
Короткий ответ — в зависимости от частоты изменений предметной области.
Высокая динамика изменений? Проводим чаще.
Предметная область относительно стабильна? Проводим реже.
Главное — делать это регулярно. Почему? Потому, что если будем назначать встречи по пересмотру модели, то такие встречи будут происходить после чего? Именно) После появления проблем. Регулярность даёт возможность обнаружить проблемы до того, как они стали реальными. Если же на регулярные встречи чаще приходим с уже случившимися проблемами дизайна — есть смысл начать проводить такие встречи чаще.
Скоро завершу оформление статьи по итогам выступления, а пока пример, когда пришлось склеить два микросервиса в один. Обычные дела)
Для тех, кто любит сестру таланта, основная мысль:
«When adding new functionality we should question whether our architecture still justifies itself. In this case, at the beginning it seemed that a ride and a prebook have a right to live as separate entities, and therefore have separate services and databases. As more requirements and features arrived, it became clear that the separation of services caused cumbersomeness.»
Рассуждения о том, где лучше расположить Event Handler.
Кто-то не задумывается об этом, другие задумываются, но не знают как поступить, третьи формулируют проблему, рассматривают альтернативы и принимают осознанное, прагматичное решение.
Расположение Event Handler рассматривается через призму следующих характеристик:
- Выделение ресурсов на разработку и поддержку
- Наличие экспертизы в предметной области
- Потребность в изменении существующих сервисов
- Выбор технологий
Рекомендую прочесть всем, кто принял решение переходить с синхронного на асинхронный стиль взаимодействия.
Кто-то не задумывается об этом, другие задумываются, но не знают как поступить, третьи формулируют проблему, рассматривают альтернативы и принимают осознанное, прагматичное решение.
Расположение Event Handler рассматривается через призму следующих характеристик:
- Выделение ресурсов на разработку и поддержку
- Наличие экспертизы в предметной области
- Потребность в изменении существующих сервисов
- Выбор технологий
Рекомендую прочесть всем, кто принял решение переходить с синхронного на асинхронный стиль взаимодействия.