compareAndSwap(l.lockName, "0", "1", l.version) }
Таким образом, блокировка снимается, только если TTL не истек.
Практикум. Реализация блокировок в etcd При реализации блокировок в etcd ключи можно применять для имен блокировок, а начальные условия записи задавать таким образом, чтобы в каждый момент времени существовал только один владелец блокировки. Для простоты поработаем с утилитой командной строки etcdctl для установки и снятия блокировки. В действительности же вам следует воспользо-ваться языком программирования. Клиенты etcd есть для всех популярных языков программирования.
Начнем с создания блокировки my-lock :
kubectl exec my-etcd-cluster-0000 -- sh -c \ "ETCD_API=3 etcdctl --endpoints=${ETCD_ENDPOINTS} set my-lock unlocked"
Это создаст в etcd блокировку my-lock с начальным значением unlocked .
164Часть II. Паттерны проектирования обслуживающих систем Допустим, Алиса и Боб хотят установить блокировку my-lock . Они пытаются записать свои имена в блокировку, исходя из того, что она изначально снята.
Алиса выполняет команду:
kubectl exec my-etcd-cluster-0000 -- sh -c \ "ETCD_API=3 etcdctl --endpoints=${ETCD_ENDPOINTS} \ set --swap-with-value unlocked my-lock alice" тем самым ставя блокировку. Теперь блокировку пытается уста-новить Боб:
kubectl exec my-etcd-cluster-0000 -- sh -c \ "ETCD_API=3 etcdctl --endpoints=${ETCD_ENDPOINTS} \ set --swap-with-value unlocked my-lock bob" Error: 101: Compare failed ([unlocked != alice]) [6] Как видим, попытка Боба установить блокировку оказалась не-удачной, так как блокировка была поставлена Алисой. Чтобы снять блокировку, Алиса записывает значение unlocked вместо alice :
kubectl exec my-etcd-cluster-0000 -- sh -c \ "ETCD_API=3 etcdctl --endpoints=${ETCD_ENDPOINTS} \ set --swap-with-value alice my-lock unlocked" Реализация владения
Блокировки хороши для установления временного владения некоторым важным компонентом. Иногда может понадобиться установить владение ресурсом на все время работы компонента. Возьмем, к примеру, кластер Kubernetes с высокой доступ-ностью. Допустим, в нем есть несколько экземпляров плани-ровщика, но только один из них активно принимает решения. Кроме того, как только процесс становится активным планиров-щиком, он остается таковым до момента отказа. Глава 9. Выбор владельца 165
Одним из подходов будет поднятие TTL до очень большой величины — скажем, недели или даже больше. Существенный недостаток такого подхода состоит в том, что в случае отказа текущего владельца блокировки новый будет выбран только по истечении TTL, неделю спустя.
Вместо этого мы можем создать возобновляемую блокировку , которая будет периодически возобновляться владельцем, по-зволяя процессу держать блокировку в течение произвольного периода времени.
Расширим предыдущую версию функции блокировки возмож-ностью возобновления ее владельцем:
func (Lock l) renew() boolean {
locked, _ = compareAndSwap(l.lockName, "1", "1", l.version, ttl) return locked
}
Для того чтобы поддерживать блокировку в течение неопре-деленного промежутка времени, придется выполнять эти дей-ствия в отдельном потоке. Обратите внимание, что блокировка возобновляется каждые TTL / 2 секунды, чтобы снизить риск ее случайного истечения в силу особенностей подсчета вре-мени:
for {
if !l.renew() {
handleLockLost()
}
sleep(ttl/2)
}
Сперва, конечно, необходимо реализовать функцию handle-LockLost() , которая прекращает деятельность, требовавшую блокировки. В рамках оркестратора контейнеров проще всего будет завершить приложение — пусть оркестратор его переза-
пустит. Это безопасно, поскольку другой экземпляр сервиса 166Часть II. Паттерны проектирования обслуживающих систем в то же время перехватит блокировку. Когда перезапущенное приложение возобновит работу, оно станет ожидать освобо-ждения блокировки.
Практикум. Реализация аренды в etcd Вернемся к ранее рассмотренному примеру работы с блокиров-ками, в частности к флагу --ttl= , используемому при создании и возобновлении блокировки. Флаг --ttl= определяет интервал времени, спустя который блокировка удаляется. Поскольку по истечении TTL секунд блокировка исчезает, будем исходить не из того, что начальное значение блокировки unlocked , а из того, что отсутствие блокировки означает, что ресурс свободен. Воспользуемся командой mk вместо set . etcdctl mk отработает успешно только в том случае, если ключа в данный момент не существует.
Следовательно, чтобы установить арендованную блокировку, Алиса выполняет такую команду:
kubectl exec my-etcd-cluster-0000 -- \
sh -c "ETCD_API=3 etcdctl --endpoints=${ETCD_ENDPOINTS} \ --ttl=10 mk my-lock alice"
Таким образом создается арендованная блокировка продолжи-тельностью 10 секунд.
Чтобы возобновить блокировку, надо выполнить следующую команду:
kubectl exec my-etcd-cluster-0000 -- \
Читать дальше