Redission — это клиент, официально рекомендованный Redis. Он обеспечивает блокировку RLock. RLock наследует интерфейс блокировки juc. Он обеспечивает такие операции, как прерывание, тайм-аут и попытка получения блокировки, а также поддерживает такие функции, как повторный вход и взаимное исключение.
Фундаментальный
Нижний уровень RLock использует хэш Redis в качестве структуры хранения.Ключ хэша используется для хранения имени блокировки, поле хэша используется для хранения идентификатора клиента, а значение, соответствующее полю, является количество повторных входов в поток.
ID клиента
Идентификатор клиента используется для различения каждого заблокированного потока и состоит из двух частей: идентификатор переменной-члена RedissonLock + идентификатор текущего потока Thread.currentThread().getId(). Если id — это UUID, ConnectionManager будет создаваться каждый раз при создании экземпляра объекта Redisson, и этот класс будет генерировать UUID (UUID.randomUUID()) при создании экземпляра, поэтому для каждого Redisson в его жизненном цикле Идентификаторы одинаковы, разница заключается в threadId, и идентификаторы между разными объектами Redisson также различаются, так что потоки в разных сервисах можно хорошо различать.
Время повторного входа потока
Вы можете обратиться к блокировке с повторным входом в Java, которая используется для указания количества раз, когда блокировка была рекурсивной.Значение >= 1. Если оно равно 0, блокировка будет удалена.
замок
Как реализовать реентерабельную блокировку
Чтобы добиться атомарности блокировки, Redisson использует форму сценария Lua для блокировки. Скрипт находится в RedissonLock#tryLockInnerAsync
if (redis.call('exists', KEYS[1]) == 0) then
redis.call('hset', KEYS[1], ARGV[2], 1);
redis.call('pexpire', KEYS[1], ARGV[1]);
return nil;
end;
if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then
redis.call('hincrby', KEYS[1], ARGV[2], 1);
redis.call('pexpire', KEYS[1], ARGV[1]);
return nil;
end;
return redis.call('pttl', KEYS[1]);
Среди них KEYS[1] — это ключ Hash, который является именем RLock, ARGV[2] — это идентификатор клиента, а ARGV[1] — это время жизни TTL Hash. Сначала определите, существует ли хэш, соответствующий KEYS[1]. Если он не существует, создайте новый хэш напрямую. Соответствующее значение в поле равно 1, что означает, что количество первых повторных входов равно 1, а время ожидания устанавливается и, наконец, возвращает null; Если хэш уже существует и элемент, поле которого равно ARGV[2], также существует, значит, поток входит в блокировку рекурсивно, и необходимо увеличить количество повторных входов в поле, и обновить время ожидания, и, наконец, вернуть ноль; Если текущая блокировка уже существует и не удерживается текущим потоком, возвращается TTL текущей блокировки.
Реализация взаимного исключения
TTL блокировки будет возвращаться каждый раз, когда выполняется блокировка. Если TTL равен нулю, это означает, что блокировка прошла успешно, и ее можно вернуть напрямую, в противном случае это означает, что другие потоки уже удерживают блокировку. Впоследствии поток будет циклически пытаться получить блокировку, пока блокировка не будет успешной. Если используется tryLock, он может вернуться сразу после истечения периода ожидания.
Продление аренды
Если блокировка установлена с периодом ожидания для удержания блокировки, блокировка будет снята после тайм-аута.Если время удержания блокировки не указано при получении блокировки, тайм-аут по умолчанию будет 30 с после получения блокировки. Чтобы предотвратить снятие блокировки до выполнения задачи, Redisson использует поток демона (задача сторожевого таймера) для периодического обновления (1/3 времени ожидания, по умолчанию 10 с, то есть каждые 10 с обновляется на 30 с). , пока поток не освободит его сам) эта блокировка Контракт обновляется после периода ожидания, то есть до тех пор, пока блокировка получена, гарантируется, что блокировка не истекает по тайм-ауту, если поток, который получает блокировку, активно выпускает его. Поскольку поток демона, получивший блокировку, и задача продолжения находятся в одном и том же потоке, когда поток, получивший блокировку, зависает, это означает, что поток, который обновляет задачу, также прекратит выполнение, и период ожидания блокировки будет не обновляться.
разблокировать
Разблокировка также выполняется с помощью Lua-скриптов, код такой же, как у метода #unlockInnerAsync:
if (redis.call('exists', KEYS[1]) == 0) then
redis.call('publish', KEYS[2], ARGV[1]);
return 1;
end;
if (redis.call('hexists', KEYS[1], ARGV[3]) == 0) then
return nil;
end;
local counter = redis.call('hincrby', KEYS[1], ARGV[3], -1);
if (counter > 0) then
redis.call('pexpire', KEYS[1], ARGV[2]);
return 0;
else
redis.call('del', KEYS[1]);
redis.call('publish', KEYS[2], ARGV[1]);
return 1;
end;
return nil;
Сначала определите, существует ли блокировка, если нет, верните nil напрямую; Если поток удерживает блокировку, текущее значение повторного входа равно -1. Если после вычисления оно больше 0, сбросить практику удержания тайм-аута и вернуть 0; Если вычисление не больше 0, удалите хэш и выполните широковещательную рассылку, чтобы уведомить сторожевой таймер о прекращении обновления и вернуть 1.
Преимущества и недостатки
преимущество
- Redisson может решить проблему обновления блокировки через механизм Watch Dog;
- По сравнению с Zookeeper Redisson имеет более высокую производительность на основе Redis и подходит для сценариев с высокими требованиями к производительности.
- Redisson реализует распределенные блокировки с повторным входом, что лучше, чем собственная реализация SET mylock userId NX PX миллисекунды + lua.Хотя основные принципы те же, это помогает нам скрывать внутренние детали выполнения. Некоторые оптимизации также были внесены в реализацию процесса, ожидающего применения ресурсов блокировки, что уменьшает количество недействительных приложений блокировки и улучшает использование ресурсов;
- Redison реализует интерфейс jucLock и полностью реализует различные функции, такие как тайм-аут, прерывание и повторный вход.
недостаток
- Redisson никак не может решить проблему простоя узлов и не может добиться согласованности ZK;
- Механизм Redison сложнее, и если вы не очень хорошо знакомы с его базовой реализацией, будет много неожиданного.
Реализация красного замка Redisson
Блокировка Redisson не решает проблему, связанную с тем, что переключение master-slave узла может привести к повторной блокировке, то есть клиент блокируется на master node, а master node в это время не работает.Из-за асинхронной репликации между master и slave, подчиненный узел не успевает реплицироваться.Когда выбран новый мастер, предыдущей блокировки нет, и другой клиент может напрямую добавить блокировку, когда он хочет работать с той же блокировкой, тогда есть два клиента, удерживающих блокировку на в то же время, так что заблокированные общие ресурсы будут заблокированы Повторное чтение, вызывая путаницу.
Redisson предоставляет блокировку RedissonRedLock для реализации RedLock, которая требует одновременной блокировки нескольких независимых экземпляров Redis. Только если успешно заблокировано более половины блокировок, считается, что они успешно заблокированы.