Содержание этой статьи переведено из главы 8 книги «Проектирование приложений, интенсивно использующих данные».
Последние несколько глав были посвящены тому, как система обрабатывает ошибки. Например, мы обсудили отказоустойчивость реплики, задержку репликации и контроль параллелизма транзакций. Когда мы понимаем различные крайние случаи, которые могут возникнуть в реальных системах, мы можем лучше с ними справляться.
Хотя в предыдущих главах много говорилось об ошибках, они все же слишком оптимистичны. В этой главе мы сделаем самое пессимистическое предположение, что «все, что может дать сбой, рано или поздно сломается».
Программирование распределенных систем коренным образом отличается от написания программного обеспечения на одной машине — основное отличие состоит в том, что существует множество новых способов отказа распределенных систем. В этой главе мы рассмотрим проблемы, возникающие на практике, и посмотрим, на что можно положиться, а на что нельзя.
В конечном счете, наша задача как инженеров состоит в том, чтобы создавать системы, которые выполняют свою работу (то есть соответствуют гарантиям, которых ожидают пользователи), несмотря на различные части, которые выходят из строя. В главе 9 мы рассмотрим несколько примеров алгоритмов, которые могут обеспечить такие гарантии в распределенных системах. Но сначала в этой главе мы должны понять проблемы, с которыми мы сталкиваемся.
Эта глава представляет собой пессимистичный и разочаровывающий обзор проблем, которые могут возникнуть в распределенных системах. Мы рассмотрим проблемы с сетью («Ненадежные сети» на стр. 277), проблемы с часами и синхронизацией («Ненадежные часы» на стр. 287) и обсудим, в какой степени их можно избежать. Последствия всех этих проблем могут сбивать с толку, поэтому мы рассмотрим, как думать о состоянии распределенной системы и как рассуждать о том, что произошло («Знание, правда и ложь» на стр. 300).
баги и частичные сбои
Когда вы пишете программу на одной машине, она обычно ведет себя предсказуемым образом: она либо работает, либо нет. Программное обеспечение с ошибками может создать впечатление, что с компьютером что-то не так (обычно проблема решается перезагрузкой), но чаще всего это результат плохо написанного программного обеспечения.
Нет фундаментальной причины, по которой программное обеспечение на одной машине должно вести себя странно: когда оборудование работает правильно, одна и та же операция всегда дает один и тот же результат (что является детерминированным). Если есть аппаратная проблема (например, плохая память или ослабленные разъемы), следствием обычно является отказ всей системы (например, «синий экран смерти», невозможность загрузки). Автономная машина с хорошим программным обеспечением обычно либо полностью функциональна, либо полностью сломана, а не где-то посередине.
Это преднамеренный выбор в дизайне компьютера: если произойдет внутренний сбой, мы скорее предпочтем полный сбой компьютера, чем вернем ложные результаты, с которыми сложно и запутанно работать. Таким образом, компьютеры скрывают нечеткую физическую реальность, от которой зависит их реализация, и создают идеализированную модель системы, которую можно идеально интегрировать с математикой. Инструкции ЦП всегда делают одно и то же: если вы записываете какие-то данные в память или на диск, эти данные остаются нетронутыми и не повреждаются случайным образом. Эта цель дизайна всегда правильно рассчитывать восходит к первым цифровым компьютерам.
Ситуация совершенно иная, когда вы пишете программное обеспечение, которое работает на нескольких компьютерах и подключено по сети. В распределенных системах мы больше не находимся в идеальной системной модели — у нас нет другого выбора, кроме как столкнуться с беспорядочными реалиями физического мира. А в реальном мире, как показывает этот анекдот, что угодно может пойти не так:
По моему ограниченному опыту, я имел дело с длительными сетевыми разделами в одном центре обработки данных (ЦОД), сбоями PDU (блока распределения питания), сбоями коммутаторов, непредвиденными сбоями питания для целых стоек, полными отказами магистрали постоянного тока, полным сбоем питания постоянного тока и водитель с гипогликемией врезался своим пикапом Ford в систему кондиционирования воздуха. Я даже не оперативник. — Кода Хейл
В распределенной системе может случиться так, что в то время как другие части системы работают нормально, некоторые части системы могут выйти из строя по какой-то непредсказуемой причине. Это называется частичным отказом. Сложность этой проблемы заключается в том, что частичные сбои недетерминированы: если вы попытаетесь сделать что-либо с несколькими узлами и сетями, иногда это может работать нормально, а иногда — непредсказуемо. Как мы увидим, вы можете даже не знать, было ли что-то успешным, потому что время, необходимое сообщению для перемещения по сети, также не определено!
Эта неопределенность и возможность частичного сбоя затрудняют работу с распределенными системами.
Облачные вычисления и суперкомпьютеры
Существует ряд принципов построения больших вычислительных систем:
-
На одном конце шкалы находится область высокопроизводительных вычислений (HPC). Суперкомпьютеры с тысячами ЦП часто используются для выполнения ресурсоемких научных вычислительных задач, таких как прогнозирование погоды или молекулярная динамика (моделирование движения атомов и молекул).
-
На другом конце спектра находятся облачные вычисления, которые не очень четко определены, но часто связаны с многопользовательскими центрами обработки данных, массовыми компьютерами (обычно Ethernet), подключенными к IP-сетям, эластичным распределением ресурсов по запросу и почасовой оплатой. .
С этими философиями подход к обработке ошибок сильно отличается. В суперкомпьютерах задания обычно время от времени проверяют свое вычислительное состояние на постоянное хранилище. Если один узел выходит из строя, обычное решение — просто остановить всю рабочую нагрузку кластера. После восстановления отказавшего узла вычисления возобновляются с последней контрольной точки. Таким образом, суперкомпьютер больше похож на одноузловой компьютер, чем на распределенную систему: он обрабатывает частичные сбои путем эскалации до полных сбоев — когда какая-либо часть системы выходит из строя, он просто крашит всю систему (как ядро на одной машине паники) .
В этой книге мы ориентируемся на систему, которая реализует Интернет-сервисы, которые обычно отлично смотрятся в суперкомпьютер:
-
Многие приложения, связанные с Интернетом онлайн, в том смысле, что им нужно иметь возможность в любое время предоставлять пользователям услуги с низкой задержкой. Сервис недоступен (например, кластер остановлен для ремонта) недопустимо. Напротив, этот оффлайн (партия) работают как погода, может остановиться и перезапустить симуляцию, но имеет небольшой эффект.
-
Суперкомпьютеры обычно строятся на выделенном оборудовании, где каждый узел очень надежен, а узлы взаимодействуют через общую память и удаленный прямой доступ к памяти (RDMA). С другой стороны, узлы в облачных сервисах строятся из обычных машин, которые обеспечивают ту же производительность при меньших затратах, но также имеют более высокую частоту отказов.
-
Сети крупных центров обработки данных часто основаны на IP и Ethernet, организованных по топологии Clos для обеспечения высокой пропускной способности пополам. Суперкомпьютеры часто используют специализированные сетевые топологии, такие как многомерные сетки и торы, которые обеспечивают лучшую производительность для рабочих нагрузок HPC с известными схемами связи.
-
Чем больше система, тем выше вероятность того, что компонент в системе выйдет из строя. Со временем сбои исправляются, и новые компоненты снова выходят из строя, но в системе с тысячами узлов разумно предположить, что в системе всегда происходят сбои. Когда стратегии обработки ошибок недостаточно эффективны, большая система может потратить много времени на восстановление после сбоев вместо того, чтобы выполнять полезную работу.
-
Это очень полезная функция для эксплуатации и обслуживания, если система может выдерживать отказы узлов и при этом продолжать работать как единое целое: например, можно выполнять последовательное обновление (см. главу 4), перезапуская по одному узлу за раз, и система продолжает предоставлять пользователям услуги без перерыва. В облачной среде, если виртуальная машина не работает, вы можете убить ее и запросить новую виртуальную машину (надеюсь, новая виртуальная машина будет быстрее).
-
При геораспределенном развертывании (данные хранятся географически близко к пользователям для уменьшения задержки доступа) связь, скорее всего, будет осуществляться через Интернет, что является медленным и ненадежным по сравнению с локальными сетями. Суперкомпьютеры обычно предполагают, что все их узлы расположены близко друг к другу.
Если мы хотим, чтобы распределенные системы работали, мы должны принять возможность частичного сбоя и заложить отказоустойчивость в программное обеспечение. Другими словами, нам нужно строить надежные системы из ненадежных компонентов. (Как обсуждалось в разделе «Надежность» на стр. 6, идеальной надежности не бывает, поэтому нам нужно понимать пределы того, что мы можем на самом деле обещать.)
Даже в небольших системах с несколькими узлами важно учитывать частичные сбои. В небольшой системе большинство компонентов, вероятно, будут работать большую часть времени. Однако рано или поздно часть системы выйдет из строя, и программе придется как-то с этим справляться. Обработка ошибок должна быть частью дизайна программного обеспечения, и оператор программного обеспечения должен знать, как программное обеспечение ведет себя при возникновении ошибки.
Неразумно полагать, что ошибки случаются редко, и надеяться только на лучшее. Важно учитывать каждую возможную ошибку (даже маловероятную) и искусственно создавать такие ситуации в тестовой среде, чтобы посмотреть, что произойдет. В распределенных системах для успеха требуются скептицизм, пессимизм и паранойя.
Создавайте надежные системы из ненадежных компонентов
Вы можете задаться вопросом, имеет ли это смысл — интуитивно система надежна настолько, насколько надежен ее наименее надежный компонент (самое слабое звено). Это не так: на самом деле построение более надежных систем на менее надежном фундаменте — старая идея в вычислительной технике. Например:
-
Коды с исправлением ошибок позволяют точно передавать цифровые данные по каналам связи с редким возникновением определенных битовых ошибок, например, из-за радиопомех в беспроводных сетях.
-
IP (интернет-протокол) ненадежен: пакеты могут быть потеряны, задержаны, дублированы или не по порядку. TCP (протокол управления передачей) обеспечивает более надежный транспортный уровень поверх IP: он обеспечивает повторную передачу потерянных пакетов, устранение дубликатов и повторную сборку пакетов в том порядке, в котором они были отправлены.
Хотя система может быть более надежной, чем ее основные части, ее надежность всегда ограничена. Например, коды с исправлением ошибок могут обрабатывать небольшое количество однобитовых ошибок, но если сигнал перегружен помехами, существует фундаментальный предел объема данных, которые можно получить по каналу связи. TCP может скрыть от нас потерю, дублирование и нарушение порядка пакетов, но он не может волшебным образом устранить задержку в сети.
Хотя более надежная высокоуровневая система не идеальна, она все же полезна, поскольку может справляться с некоторыми сложными низкоуровневыми сбоями и, следовательно, в целом упрощает устранение неполадок и обработку остальных проблем.
ненадежная сеть
Как обсуждалось во введении к части II, распределенные системы, на которых мы сосредоточимся в этой книге, — это системы без совместного использования ресурсов, т. е. набор компьютеров, соединенных сетью. Сеть — единственный способ, с помощью которого эти машины могут общаться. Мы предполагаем, что у каждой машины есть своя память и диск, и что одна машина не может получить доступ к памяти или диску другой машины (за исключением запросов к сервисам по сети).
Совместное использование ничего — не единственный способ создания систем, но он стал доминирующим способом создания интернет-сервисов по нескольким причинам: он относительно дешев, потому что не требует специального оборудования, он может использовать преимущества коммерческих облачных вычислений, это может быть избыточность в нескольких географически распределенных центрах обработки данных для обеспечения высокой надежности.
Большинство внутренних сетей Интернета и центров обработки данных (обычно Ethernet) представляют собой асинхронные пакетные сети. В такой сети один узел может послать сообщение (пакет) другому узлу, но сеть не может гарантировать, когда оно прибудет и даже прибудет ли. Если вы отправляете запрос и ожидаете ответа, многое может пойти не так (некоторые из них показаны на рис. 8-1):
- Ваш запрос мог быть утерян (возможно, кто-то отключил кабель).
- Ваш запрос может находиться в очереди для отправки позже (возможно, сеть или получатель перегружены).
- Удаленный узел может выйти из строя (может выйти из строя или потерять питание).
- Удаленный узел мог временно перестать отвечать на запросы (может возникнуть длительная пауза при сборке мусора; см. «Приостановка процессов» на стр. 295), но позже он снова начнет отвечать.
- Удаленный узел мог обработать ваш запрос, но ответ был потерян в сети (вероятно, неправильно настроен сетевой коммутатор).
- Удаленный узел мог обработать ваш запрос, но ответ был задержан и будет отправлен позже (возможно, сеть или ваша собственная машина перегружены).
Отправитель даже не может знать, был ли пакет отправлен: единственная возможность для получателя — послать ответное сообщение, которое может быть потеряно или задержано. Эти проблемы неразличимы в асинхронной сети: единственная информация, которая у вас есть, это то, что вы еще не получили ответа. Если вы отправляете запрос другому узлу и не получаете ответа, невозможно узнать, почему.
Обычный способ справиться с этим — использовать тайм-аут: через некоторое время отказаться от ожидания и предположить, что ответ не придет. Однако, когда происходит тайм-аут, вы все еще не знаете, был ли ваш запрос получен удаленным узлом (если запрос все еще находится где-то в очереди, он все еще может быть доставлен получателю, даже если отправитель отказался).
Практика отказа сети
Мы строим компьютерные сети десятилетиями — можно надеяться, что теперь мы знаем, как сделать их надежными. Однако, похоже, мы еще не успели.
Существуют систематические исследования и множество неподтвержденных данных, свидетельствующих о том, что даже в контролируемой среде, такой как центр обработки данных, управляемый компанией, сетевые проблемы могут быть очень распространенными. Исследование, проведенное в центре обработки данных среднего размера, показало, что каждый месяц происходит около 12 сетевых сбоев, при этом половина одной машины отключается, а половина всей стойки отключается. В другом исследовании измерялась частота отказов для таких компонентов, как коммутаторы верхней части стойки, коммутаторы агрегации и балансировщики нагрузки, и было обнаружено, что добавление резервного сетевого оборудования не снижает количество отказов в той мере, в какой можно было бы надеяться, потому что оно не защищает от человеческого фактора. ошибка (например, настройка неправильного коммутатора), которая является основной причиной перебоев в работе сети.
Общедоступные облачные сервисы, такие как EC2, печально известны частыми кратковременными сбоями сети, и хорошо управляемая сеть выделенного центра обработки данных будет более стабильной. Тем не менее, никто не застрахован от сбоев в работе сети: например, проблема во время обновления программного обеспечения коммутатора может вызвать перенастройку топологии сети, при которой сетевые пакеты могут задерживаться более чем на минуту. Акулы могут кусать подводные кабели и повреждать их. Другие неожиданные сбои включают сетевой интерфейс, который иногда отбрасывает все входящие пакеты, но успешно отправляет исходящие пакеты. Таким образом, только потому, что сетевое соединение работает в одном направлении, не гарантирует, что оно будет работать и в противоположном направлении.
сетевой разделКогда одна часть сети отключается от остальных из-за сбоя в сети, это иногда называют сетевым разделом или сегментацией сети. В этой книге мы используем более общий термин сетевой сбой, чтобы избежать путаницы с разделением (фрагментацией) систем хранения, как описано в главе 6.
Даже если в вашей среде сбои сети случаются редко, сам факт их возникновения означает, что ваше программное обеспечение должно уметь с ними справляться. Всегда есть вероятность того, что связь в сети прервется, этого никак не избежать.
Если обработка ошибок для сетевых сбоев не определена и не протестирована, могут возникать случайные ошибки: например, даже если сеть снова заработает, кластер может зайти в тупик и навсегда перестать обслуживать запросы или даже удалить все ваши данные. Неожиданное поведение может возникнуть, если программное обеспечение не находится в контролируемой ситуации.
Обработка сбоев сети не обязательно означает, что они терпимы: если ваша сеть обычно достаточно надежна, эффективным подходом может быть простое сообщение пользователя об ошибке, отображаемое при возникновении проблем в сети. Однако вам нужно знать, что ваше программное обеспечение будет реагировать на сетевые проблемы, и гарантировать, что система сможет восстановиться после них. Преднамеренно инициировать сетевые проблемы и проверить, насколько значима реакция системы (это идея, лежащая в основе Chaos Monkey; см. «Надежность» на стр. 6).
обнаружить сбой
Многие системы требуют автоматического обнаружения отказавших узлов. Например:
- Балансировщик нагрузки должен перестать отправлять запросы мертвым узлам.
- В реплицированной распределенной базе данных с одним лидером в случае сбоя ведущего узла необходимо повысить уровень ведомого, чтобы он стал новым ведущим (см. «Обработка сбоев узла» на стр. 152).
К сожалению, неопределенность сети затрудняет определение того, работает ли узел должным образом. В некоторых конкретных случаях вы можете получить обратную связь, в которой будет явно указано, что какой-то компонент работает неправильно:
-
Если вы можете связаться с машиной, на которой работает узел, но ни один процесс не прослушивает целевой порт (например, из-за сбоя процесса), ОС поможет закрыть или отклонить TCP-соединение, отправив пакеты RST или FIN. Однако, если узел выходит из строя в середине обработки запроса, вы не можете узнать, сколько данных на самом деле обработал удаленный узел.
-
Если происходит сбой процесса узла (или он останавливается администратором), но ОС узла все еще работает, сценарий может уведомить другие узлы о сбое, чтобы другой узел мог быстро взять на себя управление, не дожидаясь тайм-аута.
-
Если у вас есть доступ к интерфейсу управления сетевыми коммутаторами вашего центра обработки данных, вы можете запросить их, чтобы обнаружить сбои связи на аппаратном уровне (например, если удаленная машина выключена). Этот параметр недоступен, если вы подключены через Интернет, или если вы находитесь в общем центре обработки данных, но не имеете разрешения на доступ к коммутатору, или если вы не можете получить доступ к интерфейсу управления из-за проблем с сетью.
-
Если маршрутизатор определяет, что IP-адрес, к которому вы пытаетесь подключиться, недоступен, он может ответить вам пакетом, недоступным для пункта назначения ICMP. Однако у маршрутизаторов нет волшебных возможностей обнаружения сбоев — они подвержены тем же ограничениям, что и остальная часть сети.
-
Быстрая обратная связь о том, что удаленный узел не работает, полезна, но на нее нельзя рассчитывать. Несмотря на то, что TCP подтверждает, что пакет был отправлен, приложение может аварийно завершить работу до обработки данных. Если вы хотите подтвердить, что запрос был успешным, вам нужно активно ответить в самом приложении.
-
И наоборот, если что-то пойдет не так, вы можете получить ответ об ошибке на каком-то уровне, но обычно вы должны исходить из того, что вообще не получите ответа. Вы можете повторить попытку несколько раз (повторные попытки TCP прозрачны, но вы можете повторить попытку на уровне приложения), дождаться истечения времени ожидания и, наконец, объявить узел отключенным только в том случае, если в течение времени ожидания не будет получен ответ.
Тайм-ауты и бесконечные задержки
Если время ожидания - единственный надежный способ обнаружения сбоев, как долго должны быть время ожидания? К сожалению, нет простого ответа.
Длительный тайм-аут означает, что требуется долгое ожидание, прежде чем узел будет объявлен мертвым (и пользователю может потребоваться подождать или увидеть сообщение об ошибке в течение этого времени). Короткие тайм-ауты позволяют быстрее обнаруживать сбои, но несут более высокий риск ложных срабатываний, например, узел может быть замедлен только временно (например, из-за всплеска работы или сетевой нагрузки), а затем ложно признан мертвым.
Преждевременное объявление узла мертвым проблематично: если узел действительно жив и выполняет какое-то действие (например, отправляет электронное письмо), а затем другой узел вступает во владение, это действие может в конечном итоге выполниться дважды. Мы обсудим этот вопрос более подробно в разделе «Знание, правда и ложь» на странице 300 и в главах 9 и 11.
Когда узел объявляется мертвым, его обязанности должны быть переданы другим узлам, что создает дополнительную нагрузку на другие узлы и сеть. Преждевременное объявление узла мертвым может усугубить проблему, если система уже находится под большой нагрузкой. В частности, возможно, что узел на самом деле не умирает, а просто медленно отвечает из-за слишком высокой нагрузки. Перекладывание его нагрузки на другие узлы может привести к водопадным сбоям (в крайних случаях все узлы объявляют друг друга мертвыми, после чего все перестает работать).
Предположим, что сеть виртуальной системы может гарантировать максимальную задержку для пакетов — каждый пакет либо доставляется в течение определенного периода времени, либо теряется, но не более чем на d. Кроме того, предполагается, что исправные узлы гарантированно всегда обрабатывают запросы в течение периода времени r. В этом случае на каждый успешный запрос гарантированно будет получен ответ в течение 2d + r, и если за это время ответ не получен, сеть или удаленный узел считаются неработоспособными. Если ситуация описана выше, 2d + r будет разумным тайм-аутом.
К сожалению, большинство систем, которые мы используем, не имеют этих гарантий: асинхронные сети имеют бесконечную задержку (т. е. они отправляют пакеты так быстро, как только могут, но нет верхней границы того, сколько времени требуется для прибытия пакета), и большинство серверы не гарантируют обработку запросов в течение определенного времени (см. «Гарантии времени отклика» (стр. 298)). Для обнаружения сбоев большую часть времени быстро недостаточно: если тайм-аут короткий, время приема-передачи должно только увеличиться мгновенно, чтобы система стала несбалансированной.
Сеть застой и очередь
При вождении автомобиля количество времени, проведенного в дороге, имеет тенденцию меняться из-за пробок. Точно так же изменчивость задержек пакетов в компьютерных сетях часто связана с очередями:
- Если несколько разных узлов пытаются отправить пакеты в одно и то же место назначения одновременно, сетевой коммутатор должен поставить их в очередь и направить один за другим на сетевой канал назначения (как показано на рис. 8-2). В загруженном сетевом канале пакетам может потребоваться некоторое время, чтобы получить слот (это называется перегрузкой сети). Если входящих данных так много, что очередь коммутатора заполнится, пакет будет отброшен, поэтому его необходимо отправить повторно, даже если сеть работает нормально.
-
Когда пакет прибывает на целевую машину, если все ядра ЦП в данный момент заняты, входящий запрос из сети будет поставлен ОС в очередь до тех пор, пока приложение не будет готово его обработать. В зависимости от нагрузки на машину это может занять любое время.
-
В виртуализированной среде работающая операционная система обычно приостанавливается на десятки миллисекунд, пока другая виртуальная машина использует ядро ЦП. В это время виртуальная машина не может использовать какие-либо данные из сети, поэтому входящие данные ставятся в очередь (буферизуются) гипервизором, что еще больше увеличивает изменчивость сетевой задержки.
-
TCP выполняет управление потоком (также известное как предотвращение перегрузки или противодавление), когда узлы ограничивают свои собственные скорости отправки, чтобы избежать перегрузки сетевых каналов или принимающих узлов. Это означает, что отправитель поставит данные в очередь еще до того, как они поступят в сеть.
Кроме того, если TCP не подтвержден в течение определенного тайм-аута (рассчитанного на основе наблюдаемого времени приема-передачи), пакет считается потерянным, и потерянный пакет будет автоматически отправлен повторно. Хотя приложение не видит потерю пакетов и повторную передачу, оно видит возникшую в результате задержку (ожидание истечения тайм-аута, затем ожидание подтверждения повторной передачи пакета).
TCP против UDP
Некоторые чувствительные к задержкам приложения, такие как видеоконференции и передача голоса по IP (VoIP), используют UDP вместо TCP. Это компромисс между надежностью и непостоянством задержки: поскольку UDP не выполняет управление потоком и не передает повторно потерянные пакеты, он позволяет избежать некоторых причин непостоянной сетевой задержки (хотя он по-прежнему уязвим для очередей коммутаторов и планирования) эффект задержки ).
В ситуациях, когда задержанные данные бесполезны, хорошим выбором будет UDP. Например, при телефонном звонке VoIP может не хватить времени для повторной передачи потерянных пакетов, прежде чем их данные будут воспроизведены на динамике. В этом случае повторно передавать пакет нет смысла — приложению приходится заполнить слот потерянного пакета тишиной (вызвав кратковременное прерывание звука), а затем продолжить работу в потоке данных. Вместо этого повторные попытки происходят на человеческом уровне. («Можете ли вы сказать это еще раз? Просто замолчал».)
Все эти факторы способствуют изменению задержки в сети. По мере того, как системы приближаются к своей максимальной мощности, диапазон задержек в очереди расширяется: системы с большой резервной емкостью могут легко обрабатывать очереди, в то время как в интенсивно используемых системах длинные очереди могут быстро образовываться.
В общедоступном облаке и многопользовательских центрах обработки данных ресурсы совместно используются многими клиентами: сетевые каналы и коммутаторы, даже сетевые интерфейсы и ЦП (при работе на виртуальных машинах) каждого компьютера являются общими. Пакетные рабочие нагрузки, такие как MapReduce (см. главу 10), могут легко перегрузить сетевые каналы. Поскольку вы не можете контролировать или контролировать использование общих ресурсов другими клиентами, сетевая задержка может быть непостоянной, если кто-то из ваших близких использует много ресурсов.
В такой среде вы можете выбрать тайм-аут только экспериментальным путем: протестируйте и распределите время приема-передачи по сети между несколькими машинами в течение длительного периода времени, чтобы определить ожидаемую изменчивость задержки. Затем, учитывая характеристики вашего приложения, вы можете найти подходящий компромисс между задержкой обнаружения сбоя и риском преждевременного истечения времени ожидания.
Более того, вместо использования настроенного постоянного времени ожидания система способна непрерывно измерять время отклика и его изменение (джиттер) и автоматически регулировать время ожидания на основе наблюдаемого распределения времени отклика. Это можно сделать с помощью детектора ошибок Phi Accrual, который используется в Akka и Cassandra. Точно так же работает тайм-аут повторной передачи TCP.
Синхронная и асинхронная сеть
Распределенные системы были бы намного проще, если бы мы могли полагаться на то, что сеть будет доставлять пакеты с фиксированной максимальной задержкой, а не отбрасывать их. Почему мы не можем решить эту проблему на аппаратном уровне и сделать сеть надежной, чтобы программное обеспечение не учитывало эти проблемы?
Чтобы ответить на этот вопрос, интересно сравнить сеть центра обработки данных с очень надежной традиционной стационарной сетью (не сотовой, не VoIP): задержки аудиокадров и сброшенные вызовы очень редки. Для телефонных вызовов требуется стабильно низкая сквозная задержка и достаточная пропускная способность для передачи звуковых образцов речи. Было бы неплохо иметь такой же уровень надежности и предсказуемости в компьютерной сети?
Когда вы звоните по телефонной сети, она устанавливает линию: на вызов выделяется фиксированный гарантированный объем полосы пропускания по всему маршруту между двумя вызывающими абонентами. Линия остается занятой до завершения вызова. Например, сеть ISDN работает с фиксированной скоростью 4000 кадров в секунду. После установления вызова в каждом кадре (в каждом направлении) выделяется 16 бит пространства. Поэтому во время вызова каждая сторона гарантированно сможет отправлять точные 16-битные аудиоданные каждые 250 микросекунд.
Эта сеть является синхронной: даже если данные проходят через несколько маршрутизаторов, очередь не влияет на них, поскольку 16-битное пространство для вызова уже зарезервировано на следующем узле сети. А так как очереди нет, максимальная сквозная задержка сети фиксирована. Мы называем это конечной задержкой.
Разве мы не можем просто сделать сетевую задержку предсказуемой?
Обратите внимание, что линия в телефонной сети сильно отличается от TCP-соединения: линия — это фиксированный объем зарезервированной пропускной способности, которую никто не может использовать, пока линия установлена, тогда как пакеты TCP-соединения могут использовать любую доступную пропускную способность сети. . Вы можете передать TCP фрагмент данных переменного размера (например, электронное письмо или веб-страницу), и TCP передаст их в кратчайшие сроки. Когда соединение TCP простаивает, полоса пропускания не используется. Если сеть центра обработки данных и Интернет являются сетями с коммутацией каналов, максимальное время приема-передачи гарантируется после установления канала. Однако это не так: Ethernet и IP являются протоколами с коммутацией пакетов, и они страдают от очередей, вызывающих бесконечные задержки в сети. Эти протоколы не имеют концепции проводов.
Почему сети центров обработки данных и Интернет используют коммутацию пакетов? Ответ заключается в том, что они оптимизированы для импульсного трафика. Схема, подходящая для аудио- или видеовызовов, должна передавать довольно постоянное количество битов в секунду во время вызова. С другой стороны, запрос веб-страницы, отправка электронного письма или передача файла не имеют каких-либо конкретных требований к пропускной способности, мы просто хотим, чтобы это было сделано как можно быстрее.
Если вы хотите передавать файлы по сети, вы должны угадать распределение полосы пропускания. Если вы угадаете слишком мало, скорость передачи будет излишне низкой, что приведет к неиспользованию пропускной способности сети. Если вы угадаете слишком много, линия не может быть установлена (поскольку сеть не может установить канал, если ее распределение полосы пропускания не может быть гарантировано). Таким образом, использование проводной передачи для пакетной передачи данных тратит впустую пропускную способность сети и делает передачу неоправданно медленной. Напротив, TCP динамически регулирует скорость передачи данных в соответствии с доступной пропускной способностью сети.
Были попытки построить гибридные сети, поддерживающие коммутацию каналов и коммутацию пакетов, такие как ATM. Например, InfiniBand: он реализует сквозное управление потоком на канальном уровне, снижая вероятность образования очередей в сети, хотя он все еще может страдать от задержек из-за перегрузки канала. Благодаря тщательному использованию качества обслуживания (QoS, приоритезация и планирование пакетов) и управления доступом (передатчики с ограничением скорости) можно эмулировать коммутацию каналов в пакетных сетях или обеспечивать статистически ограниченные задержки.
Задержка и использование ресурсов
В более общем смысле вы можете думать о переменной задержке как о результате динамического разделения ресурсов.
Предположим, есть линия между двумя телефонными станциями, которая может делать 10 000 одновременных вызовов. Каждый канал, коммутируемый по этой линии, занимает один из слотов вызова. Таким образом, вы можете думать о линии как о ресурсе, которым могут пользоваться одновременно до 10 000 пользователей. Ресурсы распределяются статически: даже если теперь вы являетесь единственным телефоном на линии, а все остальные 9999 слотов не используются, вашей линии все равно будет выделен тот же фиксированный объем пропускной способности, как если бы линия была полностью использована.
Напротив, Интернет динамически распределяет пропускную способность сети. Отправители соревнуются за то, чтобы их пакеты прошли через сеть как можно быстрее, а сетевые коммутаторы решают, какой пакет отправить (т. е. распределяют полосу пропускания). У этого метода есть недостаток очереди, но преимущество в том, что он максимально использует линию. Провод имеет фиксированную стоимость, поэтому каждый байт, отправленный по этому проводу, дешевле, если вы используете его более полно.
Также появляется ЦП: если вы динамически разделяете каждое ядро ЦП между несколькими потоками, иногда поток должен ждать операционной системы операционной системы при работе в другом потоке, поэтому поток может быть приостановлен на разное время. Тем не менее, это более полное использование аппаратных средств, чем цикл ЦП, назначенный каждому потоку, упомянутый в «Гарантия времени отклика» на странице 298). Более высокая загрузка оборудования также является важной мотивацией при использовании виртуальных машин.
Если ресурсы являются статическими разделами (например, выделенное оборудование и выделенная полоса пропускания), в некоторых средах могут быть реализованы гарантии задержки. Однако это происходит за счет снижения использования. Другими словами, это дороже. С другой стороны, мультитентабация при динамическом распределении ресурсов обеспечивает лучшее использование, поэтому она дешевле, но имеет недостаток переменной задержки.
Переменная задержка в сети — это не закон природы, а просто результат компромисса между затратами и выгодами.
Однако такое качество обслуживания в настоящее время недоступно в многопользовательских центрах обработки данных и общедоступных облаках или при общении через Интернет. Развернутая в настоящее время технология не позволяет нам делать какие-либо гарантии относительно задержки или надежности сети: мы должны предполагать, что могут возникать перегрузки сети, очереди и бесконечные задержки. Следовательно, не существует «правильного» значения для периода тайм-аута, и его необходимо определить экспериментально.
Продолжение следует. . .