📚 Общее количество текстов: 1 Вт+ ⏳ Время чтения: 15 минут. 📢 Ключевые слова: распределенная блокировка, Redis, Etcd, ZooKeeper.
Привет всем, я Санью~~
Сегодня поговорим о Распределенной блокировка, в интернете много подобного контента, но он относительно разбросан. Подвести. итог Вниз,Поделитесь со всеми,Содержание статьи будет более,Давайте сначала поймем, о чем мы хотим говорить в интеллект-карте.
Распределенная блокировка — это способ управления синхронным доступом к общим ресурсам между распределенными системами, осуществляемый для обеспечения согласованности.
Прежде чем понимать распределенные блокировки, сначала разберитесь с блокировками потоков и блокировками процессов:
Блокировка потока: в основном используется для блокировки методов и блоков кода. Когда метод или код использует блокировку, только один поток выполняет этот метод или сегмент кода одновременно. Блокировки потоков имеют эффект только в одной и той же JVM, поскольку реализация блокировок потоков в основном зависит от общей памяти между потоками, такими как Synchronized, Lock и т. д.
Блокировка процесса: контролирует доступ нескольких процессов в одной операционной системе к общему ресурсу. Поскольку процессы независимы, каждый процесс не может получить доступ к ресурсам других процессов, поэтому блокировку процессов нельзя реализовать с помощью блокировок потоков, таких как синхронизация.
Например, пакет issync на языке Golang предоставляет базовые примитивы синхронизации, такие как взаимоисключающие блокировки.
Однако два вышеупомянутых варианта подходят для применения в одной архитектуре. Однако в распределенной системе несколько сервисных узлов и процессов развернуты на разных узлах. В это время из-за конкуренции за ресурсы две блокировки локальных ресурсов узла. неверный. .
этотиндивидуальныйкогда это необходимо Распределенная блокировка для управления доступом к нескольким ресурсам процесса в распределенной системе, поэтому Распределенная блокировка предназначена для решения распределенных взаимоисключающих вопросов!
взаимоисключающий секс легко понять,Это также самая основная функция,Просто в любое время,Только один индивидуальный клиент может получить замок.,Два отдельных клиента не могут получить блокировку одновременно.
Почему возникает взаимоблокировка? Потому что клиент, получивший блокировку, не смог снять блокировку по каким-то причинам (например, сбой компьютера и т. д.), а другие клиенты больше не могут получить блокировку, в результате чего весь процесс не может быть снят. продолжать.
Столкнувшись с этой ситуацией, конечно, есть решение!
Введите срок действия: обычно мы устанавливаем TTL(Time To Живи, время выживания) Приходитьизбежать тупика,ноэтотнельзя полностью избежать。
Простое добавление срока действия вызовет две проблемы: истечение срока действия блокировки и снятие блокировок других людей.
Блокировка дополнительной уникальности. Чтобы решить проблему снятия блокировок других людей, мы можем установить [уникальный идентификатор] для каждого клиентского процесса, чтобы мы могли проверять уникальный идентификатор на уровне приложения.
Автоматическое продление: проблема с истечением срока действия блокировки возникает из-за того, что мы не можем оценить время удержания блокировки. Если настройка короче, существует риск [раннего истечения срока действия]. Однако, если срок действия блокировки установлен слишком длинный, блокировка может не сработать. доступен в течение длительного времени.
Есть также способы справиться с этой ситуацией. Вы можете запустить демон-процесс (сторожевой таймер), чтобы определить время истечения срока действия и продлить аренду. Например, стек технологий Java может использовать Redisson для его обработки.
Реентерабельный:
Что произойдет, если поток получит блокировку, но во время выполнения попытается получить блокировку еще раз?
Да, это приводит к повторному получению блокировок, занятию ресурсов блокировок и возникновению проблем взаимоблокировок.
Давайте разберемся, что такое [реентерабельность]: это означает, что один и тот же поток может получить блокировку несколько раз, не вызывая взаимоблокировки, удерживая блокировку. То есть поток может снова получить ту же блокировку после получения блокировки A, не дожидаясь блокировки. замок, который нужно открыть.
Решение. Например, чтобы реализовать реентерабельные блокировки в распределенных блокировках Redis, вам необходимо использовать язык сценариев Lua Redis и использовать технологию счетчика ссылок, чтобы гарантировать правильность реентерабельных блокировок в том же потоке.
отказоустойчивость - это когда некоторые узлы (ноды redis и т. д.) не работают.,клиент по-прежнему может получать блокировки и снимать блокировки,Вообще говоря, есть два способа справиться с этим:
Один из них, например, etcd/zookeeper, может автоматически выполнять аварийное переключение в качестве службы блокировки, поскольку сам по себе является кластером. Другой может предоставлять несколько независимых служб блокировки. Клиент запрашивает несколько независимых служб блокировки. При сбое службы информация о блокировке. также можно получить от других служб, но этот недостаток очевиден. Клиенту необходимо запросить несколько служб блокировки.
В этой статье речь пойдет о четырех видах информации о Распределенной. Реализовано без блокировки, на основе метода С точки зрения реализации его можно разделить на два типа: мониторинг вращения и наблюдения.
На основе базы данныхи На основе Redisиз Реализация требуетклиент Когда блокировка не получена,Ввод цикла,Постоянно пытайтесь запросить, можно ли получить блокировку,До тех пор, пока не будет выполнено успешно или не истечет время ожидания.
Этот метод требует, чтобы клиентWatch отслеживал только определенный индивидуальный ключ.,Вы будете уведомлены, когда замок будет доступен.,клиент Нет необходимости в повторных обращениях,на основеzooKeeperи На основе и т.д.выполнить Распределенная Вот как работает блокировка.
Распределенная блокировкаиз Метод реализациииметьбаза данных、На основе Rediscache, ZooKeeper, Etcd и т.д., статьи в основном из этих типов Метод реализацию и объедините ее с вопросомиз, чтобы развернуть повествование!
Вы немного смущены использованием таблиц базы данных для реализации распределенных блокировок? Да, у меня тоже были некоторые сомнения, когда я собирал информацию перед написанием. Хотя мы не рекомендуем этот метод, мы также можем понять его как решение, давайте посмотрим, как это сделать. это сделано:
Например, создайте таблицу в базе данных. Таблица содержит такие поля, как имена методов, и создает уникальный индекс для поля имени метода. Если вы хотите выполнить определенный метод, используйте это имя метода, чтобы вставить запись в таблицу. Если вставка прошла успешно, вы получите Lock, удаление соответствующей строки — это снятие блокировки.
//Блокируем таблицу записей
CREATE TABLE `lock_info` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT «Первичный ключ»,
`name` varchar(64) NOT NULL COMMENT «Имя метода»,
PRIMARY KEY (`id`),
UNIQUE KEY `idx_name` (`method_name`)
) ENGINE=InnoD
В основном это достигается за счет использования поля имени в качестве уникального индекса. Уникальный индекс обеспечивает уникальность записи. Просто удалите запись после снятия блокировки.
Есть и много недостатков:
Нам не нужно здесь слишком много текста. На самом деле мы в основном используем память для реализации распределенных блокировок.
Интервьюер спросил: Знаете ли вы Распределенную? блокировка? Предположительно, большинство интервьюеров расскажут о реализации Redis Распределенная. блокировкаиз кстати, ок, перейдем к делу [На основе RedisРаспределенная блокировка】
Могу ли я просто использовать команду setnx для распределенных блокировок Redis и установить время истечения срока действия?
setnx lkey lvalue expire lockKey 30
В обычных обстоятельствах это возможно, но здесь есть проблема. Хотя setnx является атомарным, setnx + expire — нет. Другими словами, setnx и expire выполняются в два этапа, две операции: [locking и timeout] являются отдельными. завершиться неудачно, блокировка не будет снята.
Причина блокировки и установки таймаута указана в начале статьи [избежать тупика]. Если не понятно, можно прочитать подробнее.
Какова правильная команда блокировки для Redis?
//обеспечиваем атомарное выполнение команд
SET lKey randID NX PX 30000
randId — это случайная строка, генерируемая клиентом. Клиент уникален при блокировке, главным образом, чтобы избежать снятия блокировок других людей.
Давайте посмотрим на тот же процесс, как показано ниже:
Этот randId позволяет избежать снятия блокировок других людей при снятии блокировки, поскольку при снятии блокировки Клиенту необходимо сначала получить значение блокировки (randId) и определить, является ли оно тем же самым, прежде чем удалять его.
if (redis.get(lKey).equals(randId)) {
redis.del(lockKey);
}
При блокировке требуется атомарность. Как добиться атомарности при снятии блокировки🔓?
Это хороший вопрос. Мы используем атомарные команды, чтобы избежать потенциальной невозможности установить время истечения срока действия при блокировке. Для снятия блокировки также требуются две команды: Get + Del. Также существует проблема со снятием блокировок других людей.
Голова гудит😵, столько вопросов нужно обдумать, сделайте перерыв😴 когда устанете, продолжим читать!
Корень проблемы здесь в том, что блокировка оценивается на клиенте и снимается на сервере, как показано ниже:
Следовательно, оценка и удаление блокировок должны выполняться на сервере Redis. Вы можете использовать сценарии Lua, чтобы обеспечить атомарность и освободить основную логику блокировки [GET, решение, DEL]. Запишите это как сценарий Lua и позвольте Redis выполниться. Эта реализация может обеспечить атомарность этих трех шагов.
// Отпустите его только после того, как рассудите, что замок принадлежит вам.
if redis.call("GET",KEYS[1]) == ARGV[1]
then
return redis.call("DEL",KEYS[1])
else
return 0
end
Что нам делать, если Клиент1 получает блокировку, но бизнес-проблема требует длительного времени обработки и превышает срок действия блокировки?
Поскольку время выполнения бизнеса превышает время истечения срока действия блокировки, мы можем продлить блокировку, например, запустить процесс-демон, регулярно отслеживать время истечения срока действия блокировки, а когда оно подходит к концу, автоматически продлевать блокировку и сбрасывать время истечения срока действия.
Это реализовано в фреймворке Redisson, для которого требуется WatchDog (сторожевой таймер): механизм сторожевого таймера будет включен, когда время блокировки не указано при блокировке. Блокировка по умолчанию составляет 30 секунд, и она будет проверяться каждые 10 секунд, если она существует. , срок действия будет сброшен. Время составляет 30 секунд (то есть оно не будет продлено через 30 секунд).
Хм, это должно быть более стабильно! 😋
Хе-хе, это все проблемы, которые могут возникнуть из-за блокировки «одного» экземпляра Redis. Действительно, распределенные блокировки одного узла могут решить потребности большинства людей. Однако [Кластер Redis] или [Режим Sentinel] обычно используются для достижения высокой доступности Redis, что вызывает проблемы синхронизации между главным и подчиненным устройствами. 😭
Представьте себе такой сценарий:
Столкнувшись с этой проблемой, автор Redis предложил решение под названием Redlock, которое представляет собой реализацию, основанную на нескольких узлах Redis (все Master). Это решение основано на двух предпосылках:
Процесс блокировки Redlock:
Redlock снимает блокировку:
Клиент инициирует операцию снятия блокировки для всех узлов Redis.
Вопрос 1. Зачем блокировать несколько экземпляров?
в основном дляотказоустойчивость,Давайте посмотрим на пример узла «индивидуальныйMaster» на рисунке.,По сути, это индивидуальная распределенная система.,В распределенных системах всегда есть аномальные узлы.,Еще слова для блокировки из,Даже если некоторые экземпляры аварийно отключены,Остальные экземпляры успешно заблокированы,всеиндивидуальный Замок Служить Все еще доступно!
Вопрос 2. Почему необходимо рассчитывать совокупное время блокировки после успешной блокировки на шаге 3?
Операция блокировки нацелена на несколько узлов в распределении, поэтому она определенно требует больше времени, чем один экземпляр. Также необходимо учитывать задержку сети, потерю пакетов, тайм-аут и т. д. Чем больше сетевых запросов, тем выше вероятность. аномалии.
Таким образом, даже если N/2+1 узлов успешно заблокированы, если совокупное время, потраченное на блокировку, превысило время истечения срока действия блокировки, то блокировка в это время бессмысленна.
Вопрос 3: Почему нам нужно управлять всеми узлами, чтобы снять блокировку?
Главным образом для того, чтобы гарантировать, что остаточные блокировки, вызванные неисправностями узла, удалены!
Например: когда определенный узел Redis заблокирован, блокировка может не сработать по «сетевым причинам».
Или, если клиент успешно блокирует экземпляр Redis, но при чтении результата ответа проблемы с сетью приводят к сбою чтения, то блокировка фактически успешно заблокирована на Redis.
Поэтому при снятии блокировки, независимо от того, была ли она успешно заблокирована ранее, необходимо снять блокировки всех узлов.
Существует дискуссия о безопасности Redlock, я кратко упомяну об этом здесь. Если вам интересно, вы можете посмотреть:
Java Interview 365: Дебаты о безопасности RedLock Red Lock (Часть 1) 4 Согласен · 0 комментариев
Etcd — это очень надежная система хранения данных, реализованная на языке Go. Она часто хранит ключевые данные в распределенных системах. Обычно она используется в центрах настройки, обнаружении и регистрации сервисов, распределенных блокировках и других сценариях.
В этой статье в основном рассматривается, как Etcd реализует распределенные блокировки с точки зрения распределенных блокировок.
Введение в функцию Etcd:
Почему эти функции позволяют Etcd реализовывать распределенные блокировки? Потому что эти функции Etcd могут удовлетворить следующие требования для реализации распределенных блокировок:
Обладая этими знаниями и теориями, давайте посмотрим, как Etcd реализует распределенные блокировки. Поскольку я сам являюсь разработчиком Golang, мы также поместим сюда некоторый код.
Сначала посмотрите на процесс, а затем объедините его с комментариями к коду!
func main() {
config := clientv3.Config{
Endpoints: []string{"xxx.xxx.xxx.xxx:2379"},
DialTimeout: 5 * time.Second,
}
// Получить клиентское соединение
client, err := clientv3.New(config)
if err != nil {
fmt.Println(err)
return
}
// 1. Блокировка (создание аренды, автоматическое продление аренды, использование аренды для захвата индивидуального ключа )
// Используется для подачи заявки на аренду
lease := clientv3.NewLease(client)
// Оформите договор аренды на 10 лет.
leaseGrantResp, err := lease.Grant(context.TODO(), 10) //10s
if err != nil {
fmt.Println(err)
return
}
// Получить договор аренды
leaseID := leaseGrantResp.ID
// Подготовьте физическое лицо к отмене продления аренды из контекста
ctx, cancelFunc := context.WithCancel(context.TODO())
// Убедитесь, что автоматическое продление аренды прекратится после выхода из функции.
defer cancelFunc()
// Убедитесь, что срок аренды истечет после выхода из функции.
defer lease.Revoke(context.TODO(), leaseID)
// Автоматическое продление
keepRespChan, err := lease.KeepAlive(ctx, leaseID)
if err != nil {
fmt.Println(err)
return
}
// Обработка ответа о продлении аренды из корутины
go func() {
select {
case keepResp := <-keepRespChan:
if keepRespChan == nil {
fmt.Println("lease has expired")
goto END
} else {
// Договор аренды будет продлеваться каждую секунду.
fmt.Println("получать Автоматическое продлениеотвечать", keepResp.ID)
}
}
END:
}()
// if key Не существует, затем устанавливает его, иначе не удается захватить блокировку
kv := clientv3.NewKV(client)
// Создать транзакцию
txn := kv.Txn(context.TODO())
// если ключ не существует
txn.If(clientv3.Compare(clientv3.CreateRevision("/cron/lock/job7"), "=", 0)).
Then(clientv3.OpPut("/cron/jobs/job7", "", clientv3.WithLease(leaseID))).
Else(clientv3.OpGet("/cron/jobs/job7")) //еслисуществование ключа
// совершить транзакцию
txnResp, err := txn.Commit()
if err != nil {
fmt.Println(err)
return
}
// Определить, схвачен ли замок
if !txnResp.Succeeded {
fmt.Println("Замок занят:", string(txnResp.Responses[0].GetResponseRange().Kvs[0].Value))
return
}
// 2. Ведение бизнеса (заперто, очень безопасно)
fmt.Println("Задача обработки")
time.Sleep(5 * time.Second)
// 3. выпускать Замок(Отмена Автоматическое продление,освободить аренду)
// defer отменит продление аренды и снимет блокировку
}
Однако пакет параллелизма, предоставляемый clientv3, также реализует распределенные блокировки. Мы можем реализовать распределенные блокировки более удобно, но внутренняя логика реализации аналогична:
Структура хранения данных ZooKeeper похожа на дерево. Это дерево состоит из узлов, называемых Znode.
Процесс блокировки/разблокировки замков выглядит следующим образом.
ZooKeeper не учитывает время истечения срока действия, но использует [временные узлы]. После того, как Клиент получит блокировку, он всегда будет удерживать блокировку, пока продолжается соединение. Даже если клиент выйдет из строя, соответствующий временный узел Znode будет автоматически удален, гарантируя снятие блокировки.
Как Zookeeper обнаруживает сбой клиента?
Каждый клиент поддерживает сеанс с ZooKeeper, для поддержания которого используются регулярные такты.
Если Zookeeper не может получить пульс клиента в течение длительного времени, он посчитает, что срок действия сеанса истек, и удалит временный узел.
Конечно, это не идеальное решение
В следующем сценарии Клиент1 и Клиент2 могут получить блокировку одновременно в течение времени окна:
Хорошо, теперь перейдем к Подвести Попробуйте Zookeeper Преимущества и недостатки при использовании распределенных блокировок:
Преимущества Zookeeper:
недостаток:
Статья содержит много контента и включает в себя множество пунктов знаний. Если вы не поняли ее после одного прочтения, рекомендуется сохранить ее и прочитать несколько раз, чтобы построить структуру сценария для распределенных блокировок.
Подвести Попробуйте Бар,Основное содержание этой статьи итог Понятно Распределенная блокировка и как ее использовать для достижения Распределенной Блокировка может происходить разными способами.
база данных:проходить Создайте уникальную запись Приходить Экспресс одининдивидуальный Замок,Уникальная запись успешно добавлена,Блокировка успешно создана,Для снятия блокировки необходимо удалить запись.,Но легко столкнуться с узкими местами в производительности.,Так что в принципе база данных не используется как Распределенная блокировка.
Redis:Redisпоставлять Понятно Эффективныйизполучать Замокивыпускать Замокиздействовать,И в сочетании со скриптом Lua,Редиссон и др.,Есть ли лучший способ обработки исключений?,Потому что оно основано на памяти.,Эффективность чтения и письма также очень высока.
Etcd:Воспользуйтесь преимуществами аренды(Lease),Watch,Механизм пересмотра,Обеспечивает простой способ реализации из Распределенной блокировки.,Режим кластера позволяет Etcd обрабатывать большие объемы операций чтения и записи.,Отличная производительность,Но конфигурация сложная,Консистенция также присутствует.
Zookeeper:использоватьZooKeeperпоставлятьиз Функция синхронизации узлов Приходитьвыполнить Распределенная блокировка,И нет необходимости устанавливать срок годности,Он может автоматически обрабатывать разблокировку блокировки при нештатных обстоятельствах.
Если ваши бизнес-данные очень конфиденциальны, вы должны обратить внимание на эту проблему при использовании распределенных блокировок. Вы не можете предполагать, что распределенные блокировки на 100% безопасны.
Конечно, вам также нужно совместить это с вашим собственным бизнесом. Возможно, в большинстве случаев мы все еще используем Redis в качестве распределенной блокировки. Во-первых, мы с ним знакомы, и я думаю, что существует множество способов выполнения и обработки нештатных ситуаций. он может удовлетворить большинство бизнес-сценариев.
ссылка:
https://mp.weixin.qq.com/s/Fkga3KaU0fBv5zXM-b8JhA
https://zhuanlan.zhihu.com/p/378797329
https://www.cnblogs.com/aganippe/p/
·············· END ·············