Рубрики
Uncategorized

PHP использует redis для реализации распределенных блокировок

Автор оригинала: David Wong.

Последнее изменение: 5 июня 2019 15:59:34

  • Примечания по использованию redis + Lua-скрипта в PHP
  • Официальный документ Redis использует redis для создания распределенных блокировок
  1. Взаимное исключение: в любое время только один клиент может получить блокировку
  2. Отсутствие взаимоблокировки: клиент выходит из строя во время удержания блокировки и активно не освобождает блокировку, что может гарантировать, что другие последующие клиенты получат блокировку
  3. Идентификатор владельца блокировки: блокировка и разблокировка должны быть одним и тем же клиентом, и клиент не может снять блокировку, не принадлежащую ему самому (у блокировки должен быть идентификатор).

Если это кластер redis, вам также следует учитывать Отказоустойчивость: Пока большинство узлов redis работают нормально, клиент может блокировать и разблокировать

Ниже рассматривается только сценарий автономного развертывания redis

Если это развертывание кластера redis, вы можете использовать

Пример блокировки PHP

$redis = new Redis();
$redis->pconnect("127.0.0.1", 6379);
$redis - > auth ("password"); // password verification
$redis - > select (1); // select the database to use. There are 16 databases by default

$key = "...";
$value = "...";
$expire = 3;

//Parameter explanation
//$value locked client request ID must be unique among all clients obtaining lock Qingqiu. The third condition above must be met: locked / unlocked is the same client
//"NX" only locks when the key does not exist, which meets the condition 1: mutually exclusive
//"Ex" sets the lock expiration time, meeting the condition 2: avoid deadlock
$redis->set($key, $value, ["NX", "EX" => $expire])

Выполните приведенный выше результат кода:

  1. Блокировка, соответствующая ключу $, не существует. Запри ее
  2. Блокировка, соответствующая ключу$, уже существует, ничего не делайте

Точки, в которых легко ошибиться при блокировке:

  • Используйте setnx и истечение срока действия Сочетание

    Причина: если setnx Сбой пост-скрипта приведет к тупику

$стоимость Идентифицированный клиент:

  • Просто используйте миллисекундную метку времени UNIX + идентификатор клиента (в основном достаточно)
  • Используйте другие алгоритмы для обеспечения уникальных случайных значений генерируются

Подключение и подключение

В PHP, если вы используете pconnect При подключении к redis соединение, установленное с redis, будет сохраняться до конца жизненного цикла соответствующего процесса FPM после окончания текущего цикла объявления сценария. В то же время FPM повторно использует соединение при следующем запросе

То есть жизненный цикл этого соединения-это жизненный цикл процесса FPM, а не выполнение PHP-скрипта

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

Функция Pconnect не может использоваться в версии потока

На рисунке выше показано, что соединение между PHP FPM и redis не отключается сразу после запрос заканчивается

Пример разблокировки PHP: использование скрипта Lua

$key = "...";
$identification = "...";
//Keys and argv are global variables in Lua scripts
$script = <<< EOF
if redis.call("get", KEYS[1]) == ARGV[1] then
    return redis.call("del", KEYS[1])
else
    return 0
end
EOF;
# $result = $redis->eval($script, [$key, $identification], 1);
//If the return result is > 0, the unlocking is successful
//The transfer order of parameters in PHP is not the same as the standard. Pay attention to the distinction
//The second parameter represents the incoming keys and argv, which are distinguished by the third parameter. Keys is in the front, argv is in the back
//The third parameter indicates the number of keys passed in
$result = $redis->evaluate($script, [$key, $identification], 1);

Почему используются сценарии Lua:

  • Избегайте удаления блокировок, добавленных другими клиентами по ошибке

    Напр.. если клиент слишком долго выполняет другие операции после получения блокировки, блокировка будет снята автоматически. В этом случае необходимо, чтобы клиент не удалял блокировку, полученную другими клиентами, для чего требуется идентификация блокировки

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

Когда команда Eval выполняет код Lua, код Lua будет выполнен как команда, и redis не будет выполнять другие команды до тех пор, пока команда Eval не будет выполнена.

Из-за атомарности выполнения скрипта не выполняйте программы с чрезмерными накладными расходами в скрипте, иначе проверка повлияет на выполнение других запросов.

Разблокировка точек, подверженных ошибкам:

  • прямая дель Удалить ключ

    Причина: можно удалить блокировку, добавленную другими клиентами (в случае, если срок действия их блокировки истек)

  • получить Судите о принадлежности замка и о том, соответствует ли он требованиям del

    Причина: неатомная операция, если получить Срок действия почтового замка истек. В это время другие клиенты выполняют операцию блокировки дель Блокировка, добавленная другими клиентами, будет разблокирована по ошибке

↓ этот абзац воспроизводится из https://blog.csdn.net/zhouzme

Примечание:

  1. Редис принесет Все выполненные сценарии кэшируются в памяти
  2. Красный цвет в перезапуск При встрече Выпустить Ранее сохраненный сценарий
  3. Имена ключей и параметры, используемые в сценариях Lua, должны быть заменены ключами и argv. Никогда не пишите их в коде, если вы не абсолютно уверены, что они являются фиксированными и неизменными значениями каждый раз, когда вы их запрашиваете, особенно когда речь идет о времени и случайных числах. Вы должны заменить их параметрами, потому что Redis будет проверять, существует ли один и тот же сценарий в кэше сценариев каждый раз , когда он использует скрипт, в противном случае он будет храниться в кэше , если ваш сценарий очень длинный, и каждый запрос имеет разные значения переменных, будет создано бесчисленное множество кэшей сценариев, и вы обнаружите, что память redis увеличится. В начале, из-за слишком большого количества ключей и параметров, слишком сложно писать отдельно, поэтому легко и удобно объединять переменные в сценарий. В результате память продолжает расти, и это безумие-найти их, чтобы выяснить это, потребовалось много времени.

Семантические переменные должны быть Используйте локальные переменные То есть локальные , локальные переменные допустимы только в определенном блоке (структура управления, функция или фрагмент и т. Д.), Использование локальных переменных позволяет избежать конфликтов имен и ускорить доступ (локальные переменные и глобальные переменные в Lua хранятся по-разному)

  1. Если сценарий Lua длинный и не является локальным или локальным, рекомендуется использовать подпись SHA Это может сэкономить пропускную способность, но, похоже, не улучшает производительность напрямую. Принцип, который я понимаю, когда здесь популяризируется Xiaobai, заключается в том, что redis создаст уникальную подпись для каждого сценария, примет сценарий в качестве тела функции и использует подпись в качестве имени функции сценария, чтобы поместить его в кэш, поэтому для последующих вызовов нужно только передать подпись Sha для вызова функции, что намного проще. Подписи, созданные одним и тем же сценарием, одинаковы, поэтому подпись Sha может быть сначала сгенерирована локально, а затем один раз загружена сценарием на сервер. Только подпись должна быть сохранена и использована в программе. Кроме того, следует отметить, что при изменении сценария даже новая строка или пробел (которые легко игнорировать или неправильно использовать) должны быть перезагружены для получения новых общих ресурсов

    Примечание: получение подписи Sha-это отдельная функция. Не включайте это в свой обычный процесс. При локальной разработке Sha может быть сгенерирован, а строка записана в процессе. В одном и том же сценарии reids всегда генерирует одну и ту же подпись.

  2. Внесено Eval Параметр argv преобразуется в строку, если он изначально был числовым Если вам нужно оценить числовое суждение, такое как переменная > 0 или < 0 в вашей логике, вы должны преобразовать строку в число tonumber() Способ если (tonumber(ARGV[1]) > 0), то возвращает 1; конец;
  3. Я протестировал несколько сценариев Lua и сравнил их с конвейером, и обнаружил, что эффективность скрипта, как правило, на 30-40% выше, чем у конвейера

По сравнению с одной машиной, кластер redis должен учитывать одну Отказоустойчивость , более сложную конструкцию

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

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

Соответствующий перевод: http://ifeve.com/redis-lock/

Алгоритм красной блокировки

Чиновник дает алгоритм красной блокировки

Сценарий: В настоящее время существуют полностью независимые главные узлы redis, развернутые на разных хостах

Операция блокировки приобретения клиента:

  1. Используйте один и тот же ключ и уникальное значение (как значение) для запроса блокировок с этих n узлов redis одновременно. Время ожидания блокировок должно быть > > время ожидания (учитывая, что запрос занимает много времени). Если узел заблокирован, его следует пропустить как можно скорее
  2. Рассчитайте время, затраченное на шаге 1. Если общее затраченное время превысит тайм-аут, блокировка будет считаться неудачной. Клиенту необходимо успешно получить блокировку на большинстве (более чем половине) узлов, чтобы считаться успешным
  3. Если блокировка выполнена успешно, время действия блокировки равно первоначальному времени действия блокировки – время, затраченное на шаг 1
  4. В случае сбоя блокировки (тайм-аут или неспособность получить блокировку более чем в половине экземпляров N/2 + 1) клиент снимет блокировку с каждого узла (каждого, даже если узел ранее думал, что блокировка не удалась).

Оригинал: “https://developpaper.com/php-uses-redis-to-implement-distributed-locks/”