Принципа распределенной блокировки Redis достаточно, чтобы прочитать это шаг за шагом

интервью

Когда ты молод, ты не можешь ни во что влюбиться. Когда ты взрослый, то можно понять что угодно, если сдаться, каждый всю жизнь ищет того, кто близок его собственной душе - Мо Ян

Если у человека нет имени, сконцентрируйтесь на практике владения мечом. Автор Ма Хуа, обычный программист в имперской столице, статья родом из паблик аккаунтаКруг интересов исходного кода

предисловие

Думаю, что с распределёнными блокировками знакомы не все, не так-то просто их хорошо использовать или написать самому.

Для достижения вышеуказанных условий необходимоОставьте сценарии приложений распределенных замковВ чем разница, а также разные реализации, разные реализации распределенной блокировки

Сценарий распределенной блокировки

Если вы хотите по-настоящему понять распределенные блокировки, вам нужно объединить определенные сценарии, например, купить купон на 100 юаней для AirPods Pro накануне.

Если вы используете следующий код в качестве фоновой программы для получения купонов, давайте посмотрим, какие проблемы могут возникнуть.

Очевидно, что этот процесс небезопасен в сцене параллелизма, что приведет к тому, что купоны будут платить больше, чем ожидалось, аналогичные проблемы электронной коммерции нахватались.

Подумайте, как избежать этой проблемы распределенной перегрузки?

Блокировка мьютекса, семантика мьютекса в Java таковаПри этом на ресурсе разрешено работать только одному клиенту

такие как ключевые слова в JavaSynchronizedи ReentrantLock в составе пакета JUC Lock могут реализовывать блокировки взаимного исключения.

Блокировка JVM

Как показано на рисунке, добавление синхронизированной блокировки JVM действительно может решить проблему параллелизма на одной машине.

Однако, чтобы обеспечить высокую доступность сервисов в производственной среде, как минимумРазверните два сервиса, тогда синхронизация не будет работать, потому что ееОбласть видимости — это всего лишь одна JVM.

Распространяется только черезРаспределенная блокировкаЧтобы решить проблему совместного использования ресурсов нескольких служб

Если вы настаиваете на едином сервисе, то не надо говорить, что распределенные блокировки — это плывущие облака ☁️

Распределенная блокировка

Определение распределенной блокировки:

Убедитесь, что только один клиент может работать с общими ресурсами одновременно.

По сравнению с только что приведенным примером, независимо от того, сколько сервисов купонов развернуто, будет толькоСервис может добавлять или удалять операции на количестве купонов

Есть еще несколько требований, которые также должны быть соблюдены:

1,Взаимной блокировки не произойдет.Даже если клиент выйдет из строя, удерживая блокировку, и не разблокирует ее активно, другие клиенты могут быть гарантированно заблокированы в будущем.

2,Отказоустойчивой.Клиенты могут заблокировать и разблокировать, пока большинство узлов Redis работают и работает

3.Беда должна положить этому конец.Блокировку и разблокировку должен выполнять один и тот же клиент. Клиент не может разблокировать блокировки, добавленные другими.

Реализация распределенной блокировки условно делится на три типа: Redis, Zookeeper и база данных.В статье обсуждаются распределенные блокировки с помощью Redis.

История развития распределенных замков

Сначала подумаем об идее реализации распределенной блокировки

Прежде всего, мы должны сделать так, чтобы только один клиент (развернутый купонный сервис) оперировал количеством сложений и вычитаний одновременно.

В следующий разПосле завершения работы клиента, нужно позволитьДругие клиенты продолжают выполнять

1. После того, как клиент сохранит бит флага, если добавление прошло успешно, операция уменьшения количества купонов

2. Клиентской стороне 2 не удалось добавить бит флага, и на этот раз операция сокращения запасов не удалась (или продолжайте попытки получить и т. д.)

3. После завершения операции с купоном на стороне клиента необходимо сбросить бит флага, чтобы другие клиенты могли управлять запасами.

Первое издание SetNX

Добавьте флаг блокировки Lockkey для Redis. Если дополнение успешно, вы можете продолжать выполнять работу вычтении количества купонов, и, наконец, выпустить этот флаг

Из-за использования стартового пакета Redis, предоставленного Spring, некоторые команды не соответствуют собственным командам Redis.

setIfAbsent(key, val) -> setnx(key, val)

После добавления нескольких строчек кода вышел простой прототип распределенной блокировки

Срок действия второго издания истекает

Также очевидны недостатки первой версии выше, основанной на команде setnx для реализации распределенных блокировок, т.е.Взаимная блокировка может возникнуть при определенных обстоятельствах

Нарисуй картинку, приведи пример

На приведенном выше рисунке показано, что после того, как поток 1 успешно получает блокировку, процесс выполнения завершается аварийно.Операция снятия блокировки не выполняется, это будетсоздать взаимоблокировку

Если исключение выполнения метода приводит к повторному использованию потока, то операция разблокировки может быть помещена в блок finally.

Но есть проблема, есть тупик,Если поток, получивший блокировку, выполняется, служба принудительно остановлена ​​или сервер не работает, блокировка не будет снята.

В этом крайнем случае мы все равно должны это учитывать, ведь мы же не можем просто думать, что сервис — это не проблема, верно?

для RedisФлажок блокировки плюс срок действияЭто может хорошо предотвратить проблему взаимоблокировки и продолжать изменять программный код.

Несмотря на то чтомаленький красный флагДобавлено время истечения срока действия для распределенных блокировок, но по-прежнему не удается избежать проблем взаимоблокировки в крайних случаях.

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

Если вы хотите избежать взаимоблокировки при добавлении блокировок, добавьте флаг блокировки и добавьте команду времени истечения срока действия.Гарантировано атомарность, либо вместе все получится, либо вместе потерпите неудачу

набор третьего издания

Начиная с Redis 2.6.12, наша атомарная команда add lock предоставляет необязательныеСоставная команда набора строк

SET key value [expiration EX seconds|PX milliseconds] [NX|XX]

Необязательные параметры следующие:

  • EX:Установите время ожидания в секундах
  • PX:Установите время ожидания в миллисекундах
  • NX:Аббревиатура для ЕСЛИ НЕ СУЩЕСТВУЕТ, толькоВ предположении, что KEY не существуетзначение будет установлено
  • XX:Аббревиатура для ЕСЛИ СУЩЕСТВУЕТ, только еслиПри условии, что KEY существуетзначение будет установлено

Продолжайте улучшать применение распределенных блокировок, код выглядит следующим образом:

я использую2.0.9.RELEASEВерсия SpringBoot, RedisTemplate не поддерживает набор составных команд, поэтому временно измените на Jedis для достижения

Блокировка и установка срока действия обеспечивают атомарность, но нет ли проблем с такой распределенной блокировкой?

Представим этот сценарий на основе картинки и описания процесса

1 поток успешно получает блокировку, устанавливает время истечения в пять секунд, а затем выполняет бизнес-логику.

2. Затем первый поток получает блокировку и выполняет бизнес-процесс.Время выполнения превышает время истечения срока действия, а флаг блокировки истекает и освобождается.В это время второй поток успешно получает блокировку.

3. Однако после того, как поток завершил выполнение дела, он начинает выполнять процесс снятия блокировки, а затем снимает блокировку, полученную вторым потоком.

Если вышеуказанная проблема действительно возникает на линии, это действительно xxx.Более того, после того, как первый поток снимает блокировку потока два, третий поток получает блокировку, а затем второй поток снимает блокировку третьего потока после выполнения.

Четвертое издание проверяет ценность

Теперь мы можем только создать уникальное значение для идентификации личности клиента, нормализовать блокировку и разблокировку и добавить код~

Эта версия кода эквивалентна тому, когда мы добавляем флаг блокировки и устанавливаем uuid в качестве значения флага блокировки для каждого клиента одновременно.При разблокировке нам нужно определить, совпадает ли значение блокировки с у нашего собственного клиента, и после успешной идентификации блокировка будет снята.

Но если приведенный выше код выполняет бизнес-логику, если выдается исключение, блокировка может ждать только до истечения срока действия, мы можем поместить операцию разблокировки в блок finally.

На первый взгляд реализовано четыре версии распределенных блокировок вдоль и поперек, так что проблем возникнуть не должно, верно?

Правда в том, что при разблокировкеПоскольку оценка флагов блокировки и удаления не является атомарной, все еще могут быть ошибочные удаления.

1. После того, как поток один получает блокировку, процесс выполнения балабалит... Судя по тому, что блокировка тоже своя, то процессор переключается на другие дела, и бывает, что время истечения блокировки потока один истекает.

2, нить два в это время, приобретение распределенного замка, выполнить бизнес логику Balabala ...

3. Как только поток снова будет выделен на квант времени, операция удаления продолжится.

Путь к решению этой неатомарной операции может быть толькоРассматривайте значение элемента и флаг удаления как атомарную операцию

пятое издание луа

Очень недружественная операция удаления не предоставляет атомарных команд, поэтому нам нужно найти способ

Redis запустил функцию сценариев в версии 2.6, что позволило разработчикам использовать язык Lua для написания сценариев и передачи их в Redis для выполнения.

Каковы преимущества использования Lua-скриптов?

1. Уменьшите нагрузку на сеть

Изначально нам нужно было запрашивать несколько команд у службы Redis, мы можем написать команды в скрипте Lua, чтобы выполнение инициировало только сетевой запрос.

2. Атомарные операции

Redis будет выполнять команды сценария Lua как единое целое, и никакие другие команды не будут вставлены посередине.

3. Повторное использование (исследуйте самостоятельно)

Сценарий, отправленный клиентом, будет храниться в Redis, и другие клиенты могут повторно использовать этот сценарий без использования кода для выполнения той же логики.

Затем мы пишем простой Lua-скрипт для реализации операции атомарного удаления.

Фокус находится на сценарии LUA, давайте поговорим о логике этого

Сценарий сценария — это сценарий Lua, который мы выполняем в Redis, а два следующих списка — это KEYS и ARGV.

cache.eval(script, Lists.newArrayList(lockKey), Lists.newArrayList(lockValue));

KEYS[1]: lockKey

ARGV[1]: lockValue

Кода не много и он относительно простой, то есть логика, реализованная кодом на Java, вынесена в Lua-скрипт.

# 获取 KEYS[1] 对应的 Val
local cliVal = redis.call('get', KEYS[1])
# 判断 KEYS[1] 与 ARGV[1] 是否保持一致
if(cliVal == ARGV[1]) then 
  # 删除 KEYS[1]
  redis.call('del', KEYS[1]) 
  return 'OK' 
else
  return nil 
end

На этом уровне его можно ввести в эксплуатацию в некоторых проектах с небольшим параллелизмом.

Список дел

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

Но часть проблемы, перечисленная ниже, выходит за рамки клиентского кода.

  • Как реализовать реентерабельные блокировки

  • Как исправить тайм-аут блокировки выполнения кода

  • Проблема потери блокировки, вызванная потерей данных синхронизации узла master-slave.

Вышеуказанные проблемы будут объяснены одна за другой, когда в следующей статье будет представлен исходный код Redisson, а я, кстати, представлю всем новую.Официально рекомендованный клиент Redis: Redisson

Клиентов типа Jedis... более чем достаточно для нормального использования, но по функционалу они все равно не сравнимы с Redison.

Не рекомендуется использовать Redisson, выбирать разные клиенты по разным сценариям

Redisson предоставит распределенныеРазличные блокировки и разнообразная техническая поддержка,Заинтересованные друзья могут ознакомиться с довольно подробным введением на Giuhub.

В следующей статье будут подробно представлены принципы блокировки и разблокировки распределенных замков Redisson.

После прочтения исходного кода Redisson все просто... Затем я провел рефакторинг распределенной блокировки предыдущего проекта и добавил следующие функции на исходной основе.

  • Гарантированная атомарность между блокировкой и разблокировкой
  • Реентерабельная распределенная блокировка
  • Функция автоматического расширения распределенного замка

Вывод в конце статьи

Эта статья начинается с простейшей распределенной блокировки, шаг за шагом описывает существующие проблемы и решения и, наконец, дает в основном пригодную для использования распределенную блокировку.

Но ничто не является абсолютным.Для применения распределенных блокировок рекомендуетсяДобавьте стратегии чистой прибыли, оптимистическую блокировку и другие меры в код и таблицы базы данных.

Кроме того, всем рекомендую мощный клиент Redis, заинтересованные друзья могут его представить и попробовать 😊

В связи с ограниченным уровнем автора приветствуется обратная связь и исправление ошибок в статье, спасибо 🙏

Лайк моих друзей - самая большая поддержка для меня.Если я что-то почерпнул из прочтения статьи, то надеюсь, что смогуСтавьте лайки, комментируйте, подписывайтесь на Санлиан!

Рекомендуемое чтение: