Как бы вы реализовали рабочую очередь в etcd?
Я только начал изучать etcd, и один из вариантов использования, который упоминается в выступлениях создателей, - это система рабочих очередей.
Но как бы вы на самом деле это реализовали?
Базовая модель будет примерно такой.
1 процесс генерирует "тикеты с описанием работы" и помещает этот тикет в папку etcd, скажем "/ queue / worktickets / 00000000001 /"
1-> многие процессы прослушивают папку "/queue/worktickets/" на предмет изменений. при появлении нового рабочего билета каждый процесс попытается получить билет, создав новое значение в "/queue/locks/00000001" для блокировки этого билета. Только первый сможет создать значение блокировки.
Процесс, который создал тикет блокировки, выполняет свою работу, а затем удаляет тикет из очереди и, возможно, значение блокировки. Затем попробуйте получить следующий доступный билет из очереди. Если билетов больше нет, снова начните прослушивать изменения в папке "/queue/worktickets/".
На мой взгляд, это должно быть довольно просто реализовать, но если очередь становится большой (тикет легко генерируется, но трудно обрабатывается), то, похоже, будет много данных, передаваемых из etcd каждому клиенту. Насколько мне известно, нет никакого способа сказать, дайте мне первое значение в этой папке, которого нет в этой папке, и при этом некоторые не дают мне лучшие n элементов из папки.
Любой желающий поделиться своими мыслями по этому поводу.
2 ответа
Поэтому я нашел решение, которое я считаю надежным. Вот цели, для которых я разработал свое решение:
- Для работника должно быть эффективно (т. Е. O(1) сетевые обходы) получить элемент из рабочей очереди.
- Если работник умирает или иным образом терпит неудачу во время обработки предмета, предмет становится доступным для других работников.
Таким образом, идея состоит в том, чтобы иметь две очереди: ожидающую очередь и рабочую очередь. Первоначально все элементы находятся в очереди ожидания.
Когда работник пытается получить элемент, он передает его из очереди ожидания в рабочую очередь с помощью транзакции (доступно в etcd 3). В той же транзакции работник также создает блокировку для элемента. Замок защищен арендой, поэтому он автоматически удаляется, если работник умирает.
Если работник успешно заканчивает обработку элемента, он удаляет элемент из рабочей очереди, и все готово. В случае сбоя работника срок действия блокировки истечет, и элемент останется в рабочей очереди.
Таким образом, ожидается, что работники также будут заглядывать в рабочую очередь, когда ожидающая очередь будет исчерпана. Ожидается, что текущая очередь будет небольшой по сравнению с ожидающей, поэтому поиск элемента, который в данный момент не заблокирован (просто перечисление рабочей очереди), не будет дорогим.
Или, как @dannysauer упомянул в комментариях, у вас также может быть другой отказоустойчивый процесс, который передает элементы из рабочей очереди обратно в очередь ожидания.
Я полагаю, что вы уже решили эту проблему, но способ, которым я бы это сделал, - перечислить содержимое каталога рабочей очереди (это то, что вы получаете, когда получаете каталог в любом случае). Затем просто начните спускаться по списку, пытаясь создать блокировки с одинаковыми именами в каталоге блокировки, пока не получите блокировку. Создание блокировки "file" является атомарным, если вы используете флаг "prevExist=false", поэтому, если вы успешно его создали, именно вы блокируете этот элемент.
В идеале, вы должны иметь приблизительную оценку того, сколько времени потребуется для обработки элемента, и вы устанавливаете TTL немного дольше, чем этот (или вы обновляете TTL периодически после шагов, которые имеют время, которое вы можете оценить). Либо вы удаляете элемент из исходной очереди (и, возможно, воссоздаете его в "завершенном" каталоге), когда закончите, или срок действия вашей блокировки истекает, и кто-то другой забирает его.
Кроме того, в идеале вы должны поместить свой уникальный "идентификатор узла" (имя хоста, что угодно) в файл блокировки, чтобы ваши обновления TTL выполняли сравнение и установку, что приведет к ошибке, если вы потеряли блокировку из-за слишком долгого времени.
В рабочем каталоге предположительно будут элементы, созданные последовательно с использованием POST для каталога, в то время как очередь блокировки и завершенный каталог будут иметь элементы, созданные по имени с использованием PUT.