Запланированные задачи в кластере с использованием zookeeper
Мы используем Spring для запуска запланированных задач, который отлично работает с одним узлом. Мы хотим запустить эти запланированные задачи в кластере из N узлов таким образом, чтобы задачи выполнялись максимум одним узлом в определенный момент времени. Это для корпоративного использования, и мы можем ожидать от 10 до 20 узлов.
Я просмотрел различные варианты:
- Используйте Quartz, который, кажется, является популярным выбором для запуска запланированных задач в кластере. Недостаток: зависимость от базы данных, которую я хочу избежать.
- Используйте zookeeper и всегда запускайте запланированные задачи только на ведущем / главном узле. Недостаток: нагрузка при выполнении задачи не распределяется
- Используйте zookeeper и выполняйте запланированные задачи на всех узлах. Но перед выполнением задачи установите распределенную блокировку и отпустите ее после завершения выполнения. Недостаток: системные часы на всех узлах должны быть синхронизированы, что может быть проблемой, если приложение перегружено, что приводит к смещению системных часов.
- Используйте zookeeper и позвольте главному узлу продолжать выполнять задачу согласно расписанию и назначить ее случайному работнику. Новая задача не назначается, если предыдущая запланированная задача еще не была выполнена. Недостаток: кажется, добавляет слишком много сложности.
Я склоняюсь к использованию #3, которое представляется безопасным решением, если предположить, что узлы ансамбля zookeeper работают в отдельном кластере с синхронизацией системных часов с использованием NTP. Это также при условии, что если системные часы синхронизированы, то все узлы имеют равную вероятность получить блокировку для выполнения задачи.
РЕДАКТИРОВАТЬ: После некоторых размышлений я понимаю, что это может быть не безопасным решением, так как системные часы должны быть синхронизированы между узлами, где выполняются запланированные задачи, а не только узлы кластера zookeeper. Я говорю небезопасно, потому что узлы, на которых выполняются задачи, могут быть перегружены с GC-паузами и другими причинами, и существует вероятность того, что часы будут синхронизированы. Но опять же, я думаю, что это стандартная проблема с распределенными системами.
Не могли бы вы посоветовать, если мое понимание каждого из вариантов является точным? Или может быть есть лучший подход, чем перечисленные варианты для решения этой проблемы.
2 ответа
Ну, вы можете улучшить #3, как это.
Зоопарк обеспечивает наблюдателей. То есть вы можете установить наблюдателя на данный ZNode (скажем, в пути /some/path
). Все ваши узлы в кластере смотрят один и тот же Znode. Всякий раз, когда узел думает (как запланировано или как-то иначе), он должен теперь запустить запланированную задачу,
- Сначала это создать
PERSISTENT_SEQUENTIAL
дочерний узел под/some/path
(который смотрят все узлы). Кроме того, вы можете установить данные этого узла, как вы хотите. Это может быть строка json, указывающая подробности о задаче, которая будет запущена. Новый путь ZNode будет выглядеть/some/path/prefix_<sequence-number>
, - Затем все узлы в кластере будут уведомлены о созданном дочернем узле. Затем все они извлекают вновь созданные данные ZNode и декодируют задачу.
- Теперь каждый узел пытается получить распределенную блокировку. Тот, кто приобретет его первым, может выполнить его. После выполнения этот узел должен сообщить (скажем, создав новый ZNode в
/some/path/prefix_<sequence-number>
с именемsuccess
), что эта задача была выполнена. Затем отпустите замок. - Всякий раз, когда узел пытается выполнить задачу, прежде чем пытаться получить распределенную блокировку, он должен проверить, имеет ли этот ZNode уже
success
дочерний узел.
Этот дизайн гарантирует, что никакая задача не будет запущена дважды, проверяя дочерний узел с именем success
под заданным ZNode, созданным для уведомления о начале задачи.
Я использовал вышеуказанный дизайн для корпоративного решения. Собственно для распределенной командной структуры;-)
Zookeeper или Etcd не лучшие инструменты для этого варианта использования.
Если ваша среда позволяет вам использовать akka, вам будет проще использовать akka cluster + наименьший маршрутизатор почтового ящика или любой другой кластерный маршрутизатор, который вы предпочитаете. Затем отправьте расписание заданий в ActorRef для кластера. Проще настроить, вы можете настроить тысячи узлов в кластере, используя его (он использует плавающий протокол cassandra и использование кочевников).
Scalecube также будет делать это довольно легко, снова используя SWIM.