четверг, 25 декабря 2014 г.

22.1 ZeroMQ: надежные схемы "Запрос/Ответ". Общие положения.

(Начало - здесь)

 Пора задуматься о надежности.


Будет рассмотрены повторяемые шаблоны, позволяющие добиться надежности при использовании схемы "Запрос-Ответ". Список шаблонов:
  • "Ленивый пират": надежная схема "запрос-ответ" на стороне клиента.
  • "Простой пират": надежная схема "запрос-ответ" с использованием балансировки нагрузки.
  • "Пират-параноик": надежная схема "запрос-ответ" с использованием хартбитинга.
  • "Мажордом": сервис - ориентированная надежная очередь.
  • "Титаник": диск-ориентированная надежная оффлайн очередь.
  • "Двоичная звезда": надежный отказоустойчивый бэкап - сервер.
  • "Фрилансер": надежная схема "запрос-ответ" без брокеров.

Что же такое - "Надежность"?

Ни о какой "теории надежности" речи не пойдет.

Большинство людей, которые говорят о "надежности", не вполне представляют, что это такое. Мы можем определить надежность только в плане отказа. То есть, если мы можем справиться с определенным набором известных и понятных отказов, значит, мы надежны в отношении этих отказов. Не больше и не меньше. Теперь рассмотрим возможные случаи отказов в распределенных приложениях ZeroMQ, примерно в порядке убывания вероятности:

  • Код приложения - худший из виновных. Он может вызывать крах и завершение приложения, зависнуть и прекратить отвечать на запросы, выполняться слишком медленно, высасывать всю память и т.д..
  • Системный код - вроде тех брокеров, которых мы создаем с помощью ZeroMQ - они могут умереть по тем же причинам, что и код приложения. Системный код должен быть более надежным, чем код приложения, но он также может падать и прочие беды, и особенно - переполнять память, когда пытается буферизовать сообщения для медленных клиентов.
  • Очереди сообщений могут переполниться, обычно в системном коде, который общается с чрезвычайно медленными клиентами. Когда очередь переполняется, сообщения отбрасываются. Gолучаем "потерю" сообщений.
  • Отказ сети.  (например, вырубился WiFi или вы вышли за границы уверенного приема). В таких случаях ZeroMQ автоматически переподключается, но сообщения могут теряться.
  • Оборудование может "не потянуть" все процессы, запущенные в данной системе.
  • Отказ сети в экзотических случаях: например, когда сдыхают некоторые из портов свитча, из-за чего часть сети становится недоступной.
  • Датацентры могут вырубиться целиком из-за ударов молнии, землетрясений , пожаров, а также просто из-за проблем с питанием или охлаждением.
Сделать софт полностью устойчивым ко всем этим отказам  очень трудно и дорого. И мы не будем этим заниматься.

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

Проектирование надежности

Для простоты определим надежность как "способность правильно работать при зависаниях и крахе кода", то есть, при наступлении состояния "смерть". Мы хотим, чтобы все работало должным образом и чтобы это было нечто более сложное, чем просто сообщения. Следует рассмотреть каждую схему использования системы обмена сообщениями ZeroMQ и понять, как сделать так, чтобы она работала даже при отказе кода.

Рассмотрим их одну за другой:
  • Запрос - ответ: если сервер умирает в процессе обработки запроса, клиент может понять это, так как не получает ответа. После этого клиент может подождать и попробовать снова, попробовать найти другой сервер и так далее. Случай смерти клиента мы пока  интерпретируем как "не моя проблема" (игнорируем).
  • Издатель - подписчик: если клиент, получивший некоторые данные, умирает, то сервер ничего не знает об этом. В схеме Pub-sub от клиента к серверу никакой информации не поступает. Но клиент может связаться с сервером обходным путем (например через request-reply) и запросить повторно отправить информацию, которую клиент пропустил. Случай смерти сервера обсуждать не будем. Подписчики также могут самостоятельно определить, что они работают слишком медленно и принять меры (например, предупредить оператора и умереть).
  • Трубопровод: в случае смерти рабочего в процессе работы, вентилятор ничего не знает об этом. Трубопроводы работают в одном направлении, как мельницы. Однако, на выходе коллектора можно понять, что одна задача не отработала и отправить сообщение обратно к вентилятору "Эй, перезапусти задачу 324!". Если умирают вентилятор  или коллектор, то те, кто находится  находится выше клиентов по течению информационного потока, устают от ожидания и перезапускают все задание. Это не очень элегантно, но в действительности системный код не должен падать достаточно часто.
Далее мы рассмотрим только схему Запрос-Ответ, как наиболее простую для достижения надежности.
Базовая схема Запрос-Ответ (клиентский сокет REQ выполняет блокирующие  send/receive серверному сокету REP) довольно плохо противодействует наиболее общим типам отказов. Когда сервер падает при обработке запроса, клиент зависает. Если запрос потерялся в сети, клиент зависает.
Схема Запрос-Ответ все же гораздо лучше, чем TCP благодаря способности ZeroMQ самостоятельно восстанавливать обрыв соединений, балансировке нагрузки сообщений и т.д.. Но этого еще недостаточно для реальной работы. Единственный случай, когда мы действительно можем доверять базовой схеме Запрос-Ответ - это реализация связи между двумя нитями одного процесса, когда не может умереть ни сеть, ни отдельный сервер в сети.
Тем не менее, приложив небольшие усилия, можно превратить скромную схему Запрос - Ответ в хорошую основу для реальной работы в распределенной сети. В итоге мы получим набор надежных схем Запрос-Ответ (a set of reliable request-reply: RRR). Такие схемы будут назваться Пиратскими (как бы шутка разработчика).


Представляется, что наиболее известны три способа подключения клиентов к серверам. Каждый их них нуждается в своем подходе к надежности:
  • Множество клиентов напрямую общаются с одним сервером. Вариант применения: один сервер в известной конкретной конечной точке, с которым нужно общаться множеству клиентов. Типы отказов, которые мы стремимся обработать: крах сервера и разрыв соединений в сети.
  • Множество клиентов общаются с прокси брокером, который распределяет работу среди множеству рабочих. Вариант применения: сервис-ориентированная обработка транзакций. Типы отказов, которые мы стремимся обработать: падения и перезапуски рабочих, долгий цикл обработки задания рабочим, перегрузка рабочего, падения и рестарт очереди, разрыв соединений в сети.
  • Множество клиентов общаются с множеством серверов без промежуточных прокси.. Вариант применения: распределенные службы, такие, как DNS. Типы отказов, которые мы стремимся обработать: службы падают и перезапускаются, долгий цикл обработки, перегрузка сервиса, разрыв соединений в сети.
Каждый из способов имеет свои преимущества, и их часто используют совместно. Рассмотрим их более подробно.

Далее мы рассмотрим, как достигается надежность схемы Запрос - Ответ на стороне клиента: шаблон "Ленивый пират" (Продолжение).


Комментариев нет :

Отправить комментарий