背景:
在很多互联网产品应用中,有些场景需要加锁处理,比如:秒杀,全局递增ID,楼层生成等等。大部分是解决方案基于DB实现的,Redis为单进程单线程模式,采用队列模式将并发访问变成串行访问,且多客户端对Redis的连接并不存在竞争关系。
项目实践
任务队列用到分布式锁的情况比较多,在将业务逻辑中可以异步处理的操作放入队列,在其他线程中处理后出队,此时队列中使用了分布式锁,保证入队和出队的一致性。关于redis队列这块的逻辑分析,我将在下一次对其进行总结,此处先略过。
接下来对redis实现的分布式锁的逻辑代码进行详细的分析和理解:
1、为避免特殊原因导致锁无法释放, 在加锁成功后, 锁会被赋予一个生存时间(通过 lock 方法的参数设置或者使用默认值), 超出生存时间锁将被自动释放.
2、锁的生存时间默认比较短(秒级, 具体见 lock 方法), 因此若需要长时间加锁, 可以通过 expire 方法延长锁的生存时间为适当的时间. 比如在循环内调用 expire
3、系统级的锁当进程无论因为任何原因出现crash,操作系统会自己回收锁,所以不会出现资源丢失。
4、但分布式锁不同。若一次性设置很长的时间,一旦由于各种原因进程 crash 或其他异常导致 unlock 未被调用,则该锁在剩下的时间就变成了垃圾锁,导致其他进程或进程重启后无法进入加锁区域。
<"Lock:$name"; while(true) { $result = $this->redis->setnx($redisKey, (string)$expireAt); if($result !== false) { //对$redisKey设置生存时间 $this->redis->expire($redisKey, $expire); //将最大生存时刻记录在一个数组里面 $this->lockedNames[$name] = $expireAt; return true; } //以秒为单位,返回$redisKey 的剩余生存时间 $ttl = $this->redis->ttl($redisKey); // TTL 小于 0 表示 key 上没有设置生存时间(key 不会不存在, 因为前面 setnx 会自动创建) // 如果出现这种情况, 那就是进程在某个实例 setnx 成功后 crash 导致紧跟着的 expire 没有被调用. 这时可以直接设置 expire 并把锁纳为己用 if($ttl < 0) { $this->redis->set($redisKey, (string)$expireAt, $expire); $this->lockedNames[$name] = $expireAt; return true; } // 设置了不等待或者已超时 if($timeout <= 0 || microtime(true) > $timeoutAt) break; // 挂起一段时间再试 usleep($waitIntervalUs); } return false; } /** * 给当前锁增加指定的生存时间(秒), 必须大于 0 * * @param string 锁的标识名 * @param int 生存时间(秒), 必须大于 0 */ public function expire($name, $expire) { if($this->isLocking($name)) { if($this->redis->expire("Lock:$name", max($expire, 1))) { return true; } } return false; } /** * 判断当前是否拥有指定名称的锁 * * @param mixed $name */ public function isLocking($name) { if(isset($this->lockedNames[$name])) { return (string)$this->lockedNames[$name] == (string)$this->redis->get("Lock:$name"); } return false; } /** * 释放锁 * * @param string 锁的标识名 */ public function unlock($name) { if($this->isLocking($name)) { if($this->redis->deleteKey("Lock:$name")) { unset($this->lockedNames[$name]); return true; } } return false; } /** 释放当前已经获取到的所有锁 */ public function unlockAll() { $allSuccess = true; foreach($this->lockedNames as $name => $item) { if(false === $this->unlock($name)) { $allSuccess = false; } } return $allSuccess; } }
此类很多代码都写上了注释,只要认真理解下,就很容易懂得如何在redis实现分布式锁了。
免责声明:本站资源来自互联网收集,仅供用于学习和交流,请遵循相关法律法规,本站一切资源不代表本站立场,如有侵权、后门、不妥请联系本站删除!
《魔兽世界》大逃杀!60人新游玩模式《强袭风暴》3月21日上线
暴雪近日发布了《魔兽世界》10.2.6 更新内容,新游玩模式《强袭风暴》即将于3月21 日在亚服上线,届时玩家将前往阿拉希高地展开一场 60 人大逃杀对战。
艾泽拉斯的冒险者已经征服了艾泽拉斯的大地及遥远的彼岸。他们在对抗世界上最致命的敌人时展现出过人的手腕,并且成功阻止终结宇宙等级的威胁。当他们在为即将于《魔兽世界》资料片《地心之战》中来袭的萨拉塔斯势力做战斗准备时,他们还需要在熟悉的阿拉希高地面对一个全新的敌人──那就是彼此。在《巨龙崛起》10.2.6 更新的《强袭风暴》中,玩家将会进入一个全新的海盗主题大逃杀式限时活动,其中包含极高的风险和史诗级的奖励。
《强袭风暴》不是普通的战场,作为一个独立于主游戏之外的活动,玩家可以用大逃杀的风格来体验《魔兽世界》,不分职业、不分装备(除了你在赛局中捡到的),光是技巧和战略的强弱之分就能决定出谁才是能坚持到最后的赢家。本次活动将会开放单人和双人模式,玩家在加入海盗主题的预赛大厅区域前,可以从强袭风暴角色画面新增好友。游玩游戏将可以累计名望轨迹,《巨龙崛起》和《魔兽世界:巫妖王之怒 经典版》的玩家都可以获得奖励。
更新日志
- 中国武警男声合唱团《辉煌之声1天路》[DTS-WAV分轨]
- 紫薇《旧曲新韵》[320K/MP3][175.29MB]
- 紫薇《旧曲新韵》[FLAC/分轨][550.18MB]
- 周深《反深代词》[先听版][320K/MP3][72.71MB]
- 李佳薇.2024-会发光的【黑籁音乐】【FLAC分轨】
- 后弦.2012-很有爱【天浩盛世】【WAV+CUE】
- 林俊吉.2012-将你惜命命【美华】【WAV+CUE】
- 晓雅《分享》DTS-WAV
- 黑鸭子2008-飞歌[首版][WAV+CUE]
- 黄乙玲1989-水泼落地难收回[日本天龙版][WAV+CUE]
- 周深《反深代词》[先听版][FLAC/分轨][310.97MB]
- 姜育恒1984《什么时候·串起又散落》台湾复刻版[WAV+CUE][1G]
- 那英《如今》引进版[WAV+CUE][1G]
- 蔡幸娟.1991-真的让我爱你吗【飞碟】【WAV+CUE】
- 群星.2024-好团圆电视剧原声带【TME】【FLAC分轨】