Архитектура микрослужб позволяет изолировать ошибки с помощью четко определенных границ служб. Но, как и в любой распределенной системе, ошибки на уровне сети, оборудования и приложений являются обычным явлением. Любой компонент может быть временно недоступен из-за сервисных зависимостей. Чтобы свести к минимуму влияние частичных сбоев, нам необходимо создавать отказоустойчивые сервисы, которые корректно реагируют на такие сбои. Создание надежной системы всегда сопряжено с дополнительными затратами.
Риски микросервисной архитектуры
Архитектура микросервисов переносит логику приложения в сервисы и использует сетевой уровень для связи между ними. Эта практика замены одиночных вызовов внутри приложения межсетевым взаимодействием приводит к дополнительной задержке и сложности системы, что требует координации нескольких физических и логических компонентов. Повышенная сложность распределенных систем также приведет к более высокой частоте сетевых отказов.
Одним из самых больших преимуществ архитектуры микросервисов является то, что команды могут независимо проектировать, разрабатывать и развертывать свои сервисы. Они полностью владеют жизненным циклом службы. Это также означает, что команды не могут контролировать службу, от которой они зависят, поскольку, скорее всего, ею будет управлять другая команда. С архитектурой микросервисов мы должны иметь в виду, что сервисы провайдера могут быть временно недоступны из-за плохих версий, выпущенных другими людьми, конфигурации и других изменений и т. д.
Изящная деградация сервиса
Одно из величайших преимуществ архитектуры микросервисов заключается в том, что вы можете изолировать сбои и постепенно снижать качество обслуживания, когда компоненты выходят из строя по отдельности. Например, во время сбоя клиенты в приложении для обмена фотографиями могут не иметь возможности загружать новые изображения, но по-прежнему иметь возможность просматривать, редактировать и делиться своими существующими фотографиями.
В большинстве случаев такого постепенного снижения качества обслуживания трудно достичь из-за взаимозависимости приложений в распределенной системе, и вам необходимо применить несколько видов логики аварийного переключения (некоторые из которых будут рассмотрены далее в этой статье), чтобы учесть временные проблемы. Будьте готовы к неудачам и перерывам.
управление изменениями
Команда Google по обеспечению надежности веб-сайтов обнаружила, что около 70 процентов сбоев были вызваны изменениями в существующих системах. Когда вы что-то меняете в сервисе, выкладываете новую версию кода или меняете какую-то конфигурацию — всегда есть вероятность, что что-то сломается, или появится новый баг.
В микросервисной архитектуре сервисы зависят друг от друга. Вот почему вы должны свести к минимуму сбои и ограничить их негативные последствия. Чтобы справиться с проблемами изменений, вы можете реализовать стратегию управления изменениями и механизм автоматического отката.
Например, при развертывании нового кода или изменении какой-либо конфигурации следует сначала выполнить небольшую частичную замену, поэтапно заменяя все экземпляры службы. В течение этого времени их нужно отслеживать, и если вы видите, что они негативно влияют на ваши ключевые показатели, вам следует немедленно откатить сервис, который называется «канареечное развертывание».
Другим решением может быть запуск двух производственных сред. Вы всегда можете развернуть только один из них и указать свой балансировщик нагрузки на новый только после проверки того, что новая версия соответствует ожиданиям. Это называется сине-зеленым или красно-черным развертыванием.
Откат кода — это не плохо. Вы не должны оставлять глючный код в продакшене, а потом думать, что пошло не так. Чем раньше вы откатите свой код, если это необходимо, тем лучше.
Проверка работоспособности и балансировка нагрузки
Экземпляры постоянно запускаются, перезапускаются или останавливаются из-за сбоев, развертываний или автоматического масштабирования. Это может сделать их временно или постоянно недоступными. Во избежание проблем ваш балансировщик нагрузки должен пропускать неработоспособные экземпляры из маршрутизации, поскольку они в настоящее время не могут обслуживать клиентов или подсистемы.
Состояние экземпляра приложения можно определить с помощью внешних наблюдений. Вы можете сделать это, неоднократно вызываяGET /health
конечные точки или через самоотчеты. Современные основные решения для обнаружения служб непрерывно собирают информацию о работоспособности от экземпляров и настраивают балансировщики нагрузки для направления трафика только на исправные компоненты.
Самовосстановление
Самовосстановление может помочь приложениям восстанавливаться после ошибок. Можно сказать, что приложение самовосстанавливается, когда оно может предпринять необходимые шаги для восстановления после сбоя. В большинстве случаев это реализуется внешней системой, которая отслеживает работоспособность экземпляров и перезапускает их, если они находятся в состоянии сбоя в течение длительного периода времени. Самолечение очень полезно в большинстве случаев. Но в некоторых случаях постоянный перезапуск приложений может вызвать проблемы. Частые перезапуски могут быть неуместны в этом случае, когда ваше приложение не может обеспечить работоспособность из-за перегрузки или истечения времени ожидания подключения к базе данных.
Для этого конкретного сценария (например, при потере соединения с базой данных) может быть сложно реализовать расширенное решение для самовосстановления, которое его удовлетворяет. В этом случае вам нужно добавить в приложение дополнительную логику для обработки крайних случаев и сообщить внешним системам, что экземпляр не нужно перезапускать немедленно.
Отказоустойчивый кеш
Службы часто не работают из-за проблем с сетью и изменений в наших системах. Однако большинство этих сбоев носят временный характер из-за гарантий самовосстановления и балансировки нагрузки, и мы должны найти решение, чтобы наш сервис работал во время этих сбоев. Вот тут-то и приходит на помощь отказоустойчивый кеш, он помогает и обеспечивает наше приложение необходимыми данными в случае сбоя службы.
Отказоустойчивые кэши обычно используют две разные даты истечения срока действия: более короткое время говорит вам, как долго кэш может использоваться для истечения срока действия при нормальных обстоятельствах, а более длительное время говорит вам, как долго кэш будет по-прежнему доступен в случае сбоя службы.
Важно отметить, что отказоустойчивое кэширование можно использовать только в том случае, если служба работает лучше с устаревшими данными, чем вообще без данных.
Для настройки кэширования и аварийного переключения можно использовать стандартные заголовки ответов в HTTP.
Например, используйтеmax-age
Свойства могут указывать максимальное время, в течение которого ресурс считается действительным. использоватьstale-if-error
можно указать максимальное время, в течение которого ресурс может быть извлечен из кэша в случае сбоя.
Современные CDN и балансировщики нагрузки обеспечивают различные режимы кэширования и аварийного переключения, но вы также можете создать общую библиотеку для компаний со стандартными решениями по обеспечению надежности.
логика повтора
В некоторых случаях мы не можем кэшировать данные или хотим их изменить, но все наши операции заканчиваются неудачей. Для этого мы можем повторить нашу операцию, потому что мы можем ожидать, что ресурс восстановится через некоторое время, или наш балансировщик нагрузки отправит запрос на работоспособный экземпляр.
Вы должны быть осторожны, добавляя логику повторных попыток в свое приложение и клиент, потому что большое количество повторных попыток может ухудшить ситуацию или даже помешать вашему приложению восстановиться, например, когда служба перегружена, большое количество повторных попыток может только усугубить ситуацию.
В распределенной системе повторная попытка микросервисной системы может инициировать несколько других запросов или повторных попыток и начать каскадный эффект. Чтобы свести к минимуму влияние повторных попыток, вы должны ограничить их количество и использовать алгоритм экспоненциальной отсрочки, чтобы постоянно увеличивать задержку между повторными попытками, пока не будет достигнут максимальный предел.
Вы должны подготовить свое приложение к идемпотентной обработке, когда клиент (браузер, другие микрослужбы и т. д.) инициирует повторную попытку и клиент не знает, что операция не удалась до или после обработки запроса. Например, при повторной попытке покупки вам не следует снова взимать плату с клиента. Использование уникального идемпотентного ключа для каждой транзакции может помочь справиться с повторными попытками.
Ограничение тока и снижение нагрузки
Регулирование трафика — это метод, который определяет, сколько запросов конкретный клиент или приложение может получить или обработать за определенный период времени. Например, с помощью регулирования трафика вы можете отфильтровать клиентов и службы, вызывающие всплески трафика, или вы можете гарантировать, что ваше приложение не будет перегружено, когда автомасштабирование не может справиться с этим.
Вы также можете заблокировать трафик с более низким приоритетом, предоставив достаточно ресурсов для критических транзакций.
Существует другой тип ограничителя, называемый ограничителем одновременных запросов. Это полезно, когда у вас есть важные конечные точки, которые не должны вызываться более определенного количества раз, но вы все равно хотите их обслуживать.
Серия применений деэскалации нагрузки, чтобы всегда было достаточно ресурсов для обслуживания критических транзакций. Он резервирует некоторые ресурсы для высокоприоритетных запросов и не позволяет низкоприоритетным транзакциям использовать их. Переключатель деэскалации нагрузки основан на общем состоянии системы, а не на размере запросов отдельных пользователей. Снижение нагрузки помогает вашей системе восстановиться, потому что, когда у вас есть непредвиденные обстоятельства (
Чтобы узнать больше об ограничителях тока и снижении номинальных характеристик, я рекомендую ознакомиться с этимСтатьи Полоса.
Принцип Fail Fast и независимость
В архитектуре микросервисов мы хотим сделать наши сервисы отказоустойчивыми и независимыми друг от друга. Для изоляции ошибок на уровне службы мы можем использовать шаблон перегородки. Вы можете прочитать больше о перегородках далее в этой статье.
Мы также хотим, чтобы наши компоненты быстро выходили из строя, потому что мы не хотим, чтобы неисправный сервис отключался после истечения времени ожидания запроса. Нет ничего более разочаровывающего, чем зависшие запросы и не отвечающий пользовательский интерфейс. Это не только пустая трата ресурсов, но и влияет на пользовательский опыт. Наши сервисы звонят друг другу в цепочке вызовов, поэтому мы должны проявлять особую осторожность, чтобы предотвратить незавершенные операции до того, как эти задержки возрастут.
Первая идея, которая приходит вам в голову, — установить явный уровень тайм-аута для каждого вызова службы. Проблема с этим подходом заключается в том, что вы не можете знать, какое действительно разумное значение времени ожидания, поскольку некоторые вещи, такие как сбои сети и другие проблемы, повлияют только на одну или две операции. В этом случае вы, вероятно, не захотите отклонять эти запросы, если время ожидания истекло только для некоторых из них.
Можно сказать, что использование тайм-аутов для достижения отказоустойчивых эффектов в микросервисах — это антипаттерн, и вам следует избегать его использования. Вместо этого вы можете применить шаблон прерывателя цепи, основанный на статистике успешных и неудачных операций.
режим переборки
В промышленности используются переборки для разделения судов на секции, чтобы в случае отказа корпуса можно было герметизировать различные части корабля.
Концепция перегородок может использоваться для изоляции ресурсов при разработке программного обеспечения.
Применяя шаблон переборки, мы можем защитить ограниченные ресурсы от истощения. Например, для экземпляра базы данных с ограниченным числом соединений, если у нас есть две операции для подключения к нему, мы можем использовать два пула соединений для соединения вместо использования только одного общего пула соединений. Из-за такой изоляции клиента и ресурсов страница операции, время ожидания которой истекло или чрезмерное использование пула, не приведет к сбою других операций.
Одной из основных причин затопления Титаника был отказ конструкции его переборки, из-за которой вода могла вылиться через верхнюю часть переборки через верхнюю палубу, в результате чего весь корпус погрузился под воду.
выключатель
Чтобы ограничить продолжительность операции, мы можем использовать тайм-аут. Тайм-ауты предотвращают незавершенные операции и поддерживают отзывчивость системы. Однако использование статических гранулярных тайм-аутов в микросервисах — это анти-шаблон, потому что мы находимся в очень динамичной среде, и почти невозможно установить правильное ограничение времени, которое будет хорошо работать в любой ситуации.
В качестве альтернативы этому статическому тайм-ауту мы можем использовать автоматические выключатели для обработки ошибок. Автоматические выключатели названы в честь реальных электронных компонентов, потому что они делают то же самое. Вы можете защитить ресурсы и помочь им восстановиться с помощью автоматических выключателей. Они очень полезны в распределенных системах, где повторяющиеся сбои могут вызвать эффект снежного кома и вывести из строя всю систему.
Автоматические выключатели размыкаются, когда ошибка определенного типа возникает несколько раз за короткий промежуток времени. Разомкнутый автоматический выключатель предотвращает дальнейшие запросы — как мы обычно называем отключением цепи. Автоматические выключатели обычно закрываются через определенный период времени, в течение которого остается достаточно места для восстановления базовых служб.
Помните, что не все ошибки должны вызывать срабатывание автоматических выключателей. Например, вы можете пропустить проблемы на стороне клиента, такие как запросы с кодами ответа 4xx, но не сбои на стороне сервера 5xx. Некоторые автоматические выключатели также имеют полуоткрытое состояние. В этом состоянии служба отправляет первый запрос на проверку доступности системы, в то время как другие запросы отклоняются. Если этот первый запрос будет выполнен успешно, автоматический выключатель вернется в замкнутое состояние и разрешит поток трафика. В противном случае он остается открытым.
провал теста
Вы должны постоянно тестировать свою систему на наличие распространенных проблем, чтобы убедиться, что ваш сервис устойчив к сбоям. Вы должны часто тестировать сбои, чтобы вооружить свою команду возможностями обработки сбоев.
Для тестирования вы можете использовать внешний сервис для определения группы экземпляров и случайного завершения экземпляра в этой группе. Таким образом, вы можете подготовиться к сбою одного экземпляра, но вы даже можете закрыть весь регион, чтобы имитировать сбой облачного провайдера.
Одним из самых популярных решений для тестирования является Netflix.Инструмент устойчивости ChaosMonkey.
конец
Внедрить и запустить надежный сервис непросто. Нужно приложить много усилий, и компания должна иметь соответствующие финансовые вложения.
Существует много уровней и аспектов надежности, поэтому важно найти решение, которое лучше всего подходит для вашей команды. Вы должны сделать надежность важным фактором в процессе принятия бизнес-решений и выделить на это адекватный бюджет и время.
Основной урожай
- Динамические среды и распределенные системы (такие как микросервисы) могут привести к более частому поиску и устранению неполадок;
- Сервисы должны обеспечивать изоляцию сбоев и плавную деградацию для улучшения взаимодействия с пользователем;
- 70% простоев вызваны изменениями, откат кода — это неплохо;
- Сделайте отказ служб быстрым и независимым. Команды не контролируют службы, на которые они полагаются;
- Архитектурные шаблоны и методы, такие как кэширование, переборки, автоматические выключатели и ограничители тока, помогают создать надежную архитектуру микросервисов.