В прошлом году команда Uber Engineering опубликовала статью о своем новом механизме распределения нагрузки, разработанном для их микросервисной архитектуры.
Эта статья очень интересна с разных точек зрения. Итак, во время чтения я сделал несколько заметок, чтобы уловить свое понимание и записать вещи, в которые мне хотелось бы углубиться позже, если я не получу ответы к концу. Я неоднократно убеждался, что это лучший способ узнать что-то новое.
Что меня с самого начала привлекло, так это ссылка на вековые принципы, использованные при создании этого решения. Это то, что я люблю — заимствовать концепции/идеи из разных областей и адаптировать их для решения проблемы в другой области.
Если вас интересует отказоустойчивость и стабильность системы, я рекомендую также прочитать отличную книгу «Release It!». Майкл Т. Найгард.
Это старая, но полезная книга, в которой подробно рассматриваются стратегии, шаблоны и практические рекомендации по созданию отказоустойчивых и стабильных программных систем, а также подчеркивается, как эффективно справляться с сбоями.
Uber внедрил новое решение для сброса нагрузки под названием Cinnamon, которое использует ПИД-регулятор (механизм многовековой давности), чтобы решать, какие запросы должны обрабатываться или отклоняться службой, на основе текущей нагрузки службы и приоритета запроса.
Он не требует какой-либо настройки на уровне сервиса (хотя у меня был вопрос по этому поводу), автоматически адаптируется и намного эффективнее предыдущего решения QALM. Помните также, что архитектура микросервисов Uber не для слабонервных…
ПИД-регулятор — это инструмент, используемый в приложениях промышленного управления для регулирования температуры, расхода, давления, скорости и других переменных процесса. ПИД-регуляторы (пропорционально-интегрально-дифференциальные) используют механизм обратной связи контура управления для управления переменными процесса и являются наиболее точными и стабильными регуляторами.
Если вам нужна дополнительная информация об этой многовековой концепции, загляните в Википедию.
Теперь вернемся к статье. ПИД означает «пропорциональный», «интегральный» и «производный». В их случае они используют компонент, известный как ПИД-регулятор, для мониторинга работоспособности службы (входных запросов) на основе трех компонентов (или показателей).
Термин «пропорциональный» указывает на то, что предпринятое действие пропорционально текущей ошибке. Проще говоря, это означает, что применяемая коррекция прямо пропорциональна разнице между желаемым и фактическим состоянием. Если ошибка велика, корректирующее действие пропорционально велико.
Когда конечная точка перегружена, фоновая горутина начинает отслеживать приток и отток запросов в приоритетную очередь.
Таким образом, компонент «Пропорциональный» (P) в устройстве распределения нагрузки регулирует скорость сброса нагрузки в зависимости от того, насколько текущий размер очереди отличается от целевого или желаемого размера очереди. Если очередь больше желаемого, происходит большее отбрасывание; если он меньше, выделение уменьшается.
Я так понимаю.
Задача ПИД-регулятора — минимизировать количество запросов в очереди, тогда как задача автонастройки — максимизировать пропускную способность службы, не жертвуя задержками ответа (слишком сильно).
Хотя в тексте явно не упоминается «Интеграл (I)» в контексте размера очереди, это указывает на то, что роль ПИД-регулятора заключается в минимизации количества запросов в очереди. Минимизация запросов в очереди соответствует цели компонента Integral по устранению накопленных ошибок с течением времени.
Чтобы определить, перегружена ли конечная точка, мы отслеживаем, когда в последний раз очередь запросов была пустой, и если она не была очищена в течение последних, скажем, 10 секунд, мы считаем, что конечная точка перегружена (по примеру Facebook).
В устройстве распределения нагрузки это может быть связано с решениями, связанными с историческим поведением очереди запросов, например временем с момента ее последнего пустования.
Честно говоря, мне это не совсем понятно. Должен сказать, это немного расстраивает. Хотя они упоминают об использовании многовекового механизма, было бы полезно, если бы они четко указали, какая часть соответствует чему и как он работает. Я не хочу принижать ценность их замечательной статьи. Это всего лишь моя напыщенная речь... В конце концов, я француз... ;)
Я думаю, что это легче идентифицировать.
В классическом ПИД-регуляторе (пропорционально-интегрально-дифференциальный) действие «Производная (D)» особенно полезно, когда вы хотите, чтобы контроллер предвидел будущее поведение системы на основе текущей скорости изменения ошибки. Это помогает гасить колебания и повышать стабильность системы.
В контексте устройства распределения нагрузки и ПИД-регулятора, упомянутых в статье, компонент Derivative, скорее всего, используется для оценки того, насколько быстро заполняется очередь запросов. Тем самым это помогает принимать решения, направленные на поддержание стабильной системы и предотвращение внезапных или непредсказуемых изменений.
Компонент отклонения имеет две обязанности: а) выяснить, перегружена ли конечная точка, и б) если конечная точка перегружена, уменьшить процент запросов, чтобы убедиться, что очередь запросов как можно меньше. Когда конечная точка перегружена, фоновая горутина начинает отслеживать приток и отток запросов в приоритетную очередь. На основе этих чисел он использует ПИД-регулятор для определения соотношения запросов к сбросу. ПИД-регулятор очень быстро (поскольку требуется очень мало итераций) находит правильный уровень, и как только очередь запросов будет опустошена, ПИД-регулятор гарантирует, что мы только медленно уменьшаем коэффициент.
В упомянутом контексте ПИД-регулятор используется для определения соотношения запросов к сбросу при перегрузке конечной точки, а также отслеживает приток и отток запросов. Производный компонент ПИД-регулятора, который реагирует на скорость изменения, неявно участвует в оценке того, насколько быстро заполняется или опустошается очередь запросов. Это помогает принимать динамичные решения для поддержания стабильности системы.
В контексте определения перегрузки интегральный компонент может быть связан с отслеживанием того, как долго очередь запросов находилась в непустом состоянии. Это согласуется с идеей накопления интеграла сигнала ошибки с течением времени.
«Интеграл — в зависимости от того, как долго запрос находится в очереди…»
С другой стороны, производная составляющая связана со скоростью изменения. Он реагирует на то, насколько быстро меняется состояние очереди запросов.
«Дериватив — отказ в зависимости от того, насколько быстро заполняется очередь…»
Интегральный компонент подчеркивает продолжительность непустого состояния, а производный компонент учитывает скорость изменения очереди.
В конце игры они используют эти три показателя, чтобы определить порядок действий по запросу.
У меня вопрос: как они сочетают эти три компонента, если вообще совмещают? Мне тоже интересно понять, как они за ними следят.
Тем не менее, думаю, у меня появилась идея…
Конечная точка на периферии помечается приоритетом запроса, и он распространяется от периферии на все нижестоящие зависимости через Jaeger . Распространяя эту информацию, все службы в цепочке запросов узнают о важности запроса и о том, насколько он важен для наших пользователей.
Первая мысль, которая приходит на ум, — это то, что это будет легко интегрироваться в архитектуру сервисной сетки.
Я ценю концепцию использования распределенной трассировки служб и заголовков для распространения приоритета запроса. В этом смысле, зачем выбирать общую библиотеку с этой зависимостью, добавленной к каждому микросервису, вместо того, чтобы размещать ее вне службы, например, в виде плагина Istio? Учитывая преимущества, которые он предлагает: независимые циклы выпуска/развертывания, поддержка нескольких языков и т. д.
Вот некоторые дополнительные мысли:
Ну, я предвзят, поскольку я не большой поклонник разделяемых библиотек хотя бы потому, что считаю, что они усложняют процесс выпуска/развертывания. Однако я не уверен, стоит ли учитывать какой-либо аспект конфигурации, специфичный для службы. Возможно, они настраивают, как долго служба должна ждать, прежде чем начать обработку запроса и завершить его?
Возможно, один из аспектов, который стоит проверить, — это процесс принятия решений эжектором.
Насколько я понимаю, он определяет, отклонять ли запрос, на основе ПИД-регулятора, который локализован в сервисе. Есть ли вариант более глобального подхода? Например, если известно, что одна из нижестоящих служб в конвейере перегружена (из-за собственного PID-контроллера), может ли какая-либо вышестоящая служба принять решение отклонить запрос до того, как он достигнет этой перегруженной службы (что может быть на n шагов дальше по конвейеру) путь)?
Это решение может быть основано на значении, возвращаемом ПИД-регулятором или автонастройкой нижестоящей службы.
Теперь я размышляю над различными упомянутыми аспектами, пока они завершают статью и приводят некоторые цифры, демонстрирующие эффективность их системы, что весьма впечатляет.
В какой-то момент они упоминают, что «каждый запрос имеет тайм-аут в 1 секунду».
Мы запускаем тесты продолжительностью 5 минут, в которых отправляем фиксированное количество RPS (например, 1000), где 50% трафика приходится на уровень 1, а 50% — на уровень 5. Каждый запрос имеет таймаут в 1 секунду.
В распределенных системах принято связывать запрос с определенным временем истечения срока действия или крайним сроком, при этом каждая служба на пути обработки отвечает за соблюдение этого ограничения по времени. Если время истечения срока действия достигнуто до завершения запроса, любая служба в цепочке имеет возможность прервать или отклонить запрос.
Я предполагаю, что этот 1-секундный тайм-аут привязан к запросу, и каждая служба, в зависимости от того, где мы находимся в этот срок, может решить прервать запрос. Это глобальный показатель, поскольку он агрегируется по сервисам. Я думаю, что это перекликается с тем, что я говорил ранее о наличии глобального представления о полной работоспособности системы или зависимостях, чтобы принять решение как можно скорее прервать запрос, если у него нет возможности завершиться из-за одной из служб ниже. путь.
Можно ли возвращать «работоспособность» нижестоящих служб (включая данные от их локальных ПИД-контроллеров) в виде заголовков, прикрепленных к ответам, и использовать их для создания более развитого механизма автоматического выключателя/раннего упреждающего отключения?
Наконец, мне интересно узнать больше о предыдущем подходе, потому что, судя по описанию, данному в этой статье, он кажется разумным.
Когда вы изучаете показатели производительности и задержек, не возникает сомнений в том, какой из них, QALM или Cinnamon, работает лучше всего. Обратите внимание, что в статье упоминается ссылка на подход QALM. Наверное, стоит начать с этого ;)
Как всегда, эти подходы не для всех. Архитектура и нагрузка Uber какие-то свои. На самом деле мне не терпится прочитать следующие статьи этой серии, особенно чтобы узнать больше о ПИД-регуляторе и автотюнере.
С помощью Cinnamon мы создали эффективное устройство распределения нагрузки, которое использует проверенные веками методы для динамического установления пороговых значений для отклонения и оценки мощности сервисов. Он решает проблемы, которые мы заметили в QALM (и, следовательно, в любом устройстве распределения нагрузки на основе CoDel), а именно: Cinnamon способен:
- Быстро найти стабильный процент отказов
- Автоматическая настройка мощности сервиса
- Использоваться без установки каких-либо параметров конфигурации.
- Нести очень низкие накладные расходы
Что интересно в этом подходе, так это то, что они рассматривают все запросы, подлежащие обработке, чтобы решить, что делать с каждым новым входным запросом, поскольку они используют (приоритетную) очередь. Как уже упоминалось, мне любопытно, может ли этот механизм учитывать работоспособность всех зависимых служб на основе тех же показателей PID…
В этой статье есть и другие интересные аспекты, например, как они измеряют эффект своих стратегий и сравнение с предыдущим подходом. Однако оно не требует от меня более подробных заметок, чем уже изложенные. Итак, я настоятельно рекомендую вам прочитать оригинальную статью .
Нашли эту статью полезной? Следуйте за мной в Linkedin , Hackernoon и Medium ! Пожалуйста 👏 поделитесь этой статьей!
Также опубликовано здесь.