原文連接 https://www.gwalker.cn/article-1482d08g8952e122a6044ab1c87bdd95.html
設(shè)計(jì)思路
任何一個(gè)請(qǐng)求進(jìn)來(lái)時(shí);先向redis申請(qǐng)"加鎖操作"
若加鎖成功則進(jìn)行執(zhí)行業(yè)務(wù)邏輯,執(zhí)行完畢之后惰瓜,釋放鎖;否則 不執(zhí)行業(yè)務(wù)邏輯妈经。
設(shè)計(jì)細(xì)節(jié)
1 加鎖時(shí)設(shè)置過(guò)期時(shí)間;這樣可保證運(yùn)行中間出現(xiàn)異常,沒(méi)有進(jìn)行到鎖刪除操作。一定時(shí)間之后,鎖會(huì)自動(dòng)釋放,防止死鎖肩榕。
2 釋放鎖操作垮抗,判斷鎖是否是自己加的氏捞。若是自己加的,才可以進(jìn)行釋放操作冒版。防止鎖超時(shí),釋放的為其他請(qǐng)求生成的鎖液茎。
實(shí)現(xiàn)細(xì)節(jié)
1 因?yàn)閜hp-redis擴(kuò)展提代的方法為 public function set( $key, $value, $timeout = 0 ) {}
,
實(shí)現(xiàn)不了 SET key value [EX seconds] [PX milliseconds] [NX|XX]
這樣的原生命令。所以只能使用$redis->rawCommand()方法執(zhí)行,原生redis指令辞嗡。
2 釋放鎖捆等,原子性操作,需要判斷鎖為自己設(shè)定的续室,才可進(jìn)行刪除栋烤。redis沒(méi)有提供這樣的原子性操作命令。采取$redis->eval()發(fā)送lua腳本去實(shí)現(xiàn)挺狰。
// 分布式鎖
$redis = new Redis();
$redis->connect('172.17.0.3', 6379);
// 鎖名
$key = 'lock:xxx';
// 鎖值
$tag = uniqid();
// 加鎖 且鎖的有效時(shí)間為5秒,NX(意為 NOT EXISTS)如果鎖不存在,才可設(shè)置成功班缎。
$re = $redis->rawCommand('set', $key, $tag, 'EX', 5, 'NX');
if(!$re){
die('系統(tǒng)繁忙,請(qǐng)稍后重試');
}
// do_something(); // 待執(zhí)行的業(yè)務(wù)邏輯
// 釋放鎖
$luaScript =<<<SCRIPT
if redis.call("get",KEYS[1]) == ARGV[1] then
return redis.call("del",KEYS[1])
else
return 0
end
SCRIPT;
$redis->eval($luaScript,[$key,$tag],1);