Aquí está lo que yo uso en la clase de trabajo, Redis encerrado en dos formas, con el fin de garantizar la atomicidad desbloqueado por lo que sólo lua + ReDiS camino
Defectos: Mientras se resuelve punto muerto, pero el período de tiempo de ejecución de negocios o hay más de cuestiones de bloqueo multi-cliente de bloqueo.
Sin embargo, esta clase ha cumplido con mis necesidades de negocio actuales
Mejores soluciones pueden hacer referencia a los dos artículos siguientes:
https://redis.io/topics/distlock (descripción algorítmica de Redlock)
https://mp.weixin.qq.com/s/1bPLk_VZhZ0QYNZS8LkviA
la implementación del código:
RedisLock clase
{
/ **
* @ var identificación de bloqueo actual, para el desbloqueo
* /
privada $ _lockFlag;
$ _redis privadas;
__construct función pública ($ host = '127.0.0.1', $ port = '6379', $ passwd = '')
{
$ this -> _ Redis = new Redis ();
$ this -> _ redescubrir> connect ($ host, $ puerto);
if ($ passwd) {
$ this -> _ redescubrir> auth ($ passwd);
}
}
bloqueo de la función pública ($ clave, expiran $ = 5)
{
$ ahora = time ();
$ ExpireTime = $ $ + expira ahora;
if ($ this -> _ redescubrir> setnx ($ clave, $ ExpireTime)) {
$ this -> _ lockFlag = $ ExpireTime;
return true;
}
// obtener un bloqueo en el tiempo de expiración
$ = $ currentLockTime la this -> _ redescubrir> get ($ key);
SI (currentLockTime $ <$ ahora) {
/ * Solución
C0 tiempo extra, también se mantiene el bloqueo, se añadió C1 / C2 / ... método dentro de solicitudes simultáneas en
C1 / C2 GetSet realizar un proceso (proceso porque GetSet atómico,
por lo que el valor de dos solicitud vuelto desigual debe asegurar que el C1 / C2 sólo adquieren una cerradura) * /
$ $ = oldLockTime la this -> _ redescubrir> GetSet (clave $, $ ExpireTime);
SI (== $ $ currentLockTime oldLockTime) {
-;> _ lockFlag = $ $ ExpireTime el presente
; el retorno a la verdadera
}
}
falso retorno;
}
lockByLua función pública ($ clave, expiran $ = 5)
{
$ script = <<< EOF
clave local = teclas [1]
valor local = ARGV [1]
ttl local = ARGV [2]
si (redis.call ( 'setnx' llave, valor) == 1) y luego
de regreso redis.call ( 'expirará', llave, ttl)
elseif (redis.call ( 'ttl', clave) == -1) entonces
redis.call retorno ( 'expirará', clave, TTL)
final
devolver 0
EOF;
$ this -> _ lockFlag = MD5 (microtime (verdadero));
return $ this -> _ eval ($ guión, [$ key, $ this -> _ lockFlag, $ expirará]);
}
la función pública de desbloqueo ($ clave)
{
$ script = <<< EOF
clave local = teclas [1]
valor local = ARGV [1]
si (redis.call ( 'existe', clave) == 1 y redis.call ( 'conseguir', clave) == valor)
a continuación,
volver redis.call ( 'del', clave)
final
devolver 0
EOF;
if ($ this -> _ lockFlag) {
return $ this -> _ eval ($ guión, [$ key, $ this -> _ lockFlag]);
}
}
_eval función privada ($ guión, array $ params, $ keyNum = 1)
{
$ de hash = $ this -> _ redescubrir> guión ( 'carga', $ script);
return $ this -> _ redescubrir> evalSha ($ hachís, $ params, $ keyNum);
}
}
$ RedisLock RedisLock = new ();
$ = clave 'bloqueo';
if ($ redisLock-> lockByLua ($ key)) {
// hacer ...
$ redisLock-> desbloqueo ($ key);
}