原理 acquire 使用mongodb的原子操作findAndModify更新lock表中的locked值为true。 查询条件: expireAt 已过期 更新条件: locked => true expireAt 为当前时间加1分钟 使用upsert选项,如果不存在则插入,注意,upsert操作有可能因唯一索引冲突而失败,若发生这种情况应继续尝试加锁 同时取出修改以前的值oldLock 判断oldLock 如果oldLock为true,或者oldLock.locked为false,表示锁以前不存在或没有被锁,加锁成功。 如果oldLock.expireAt存在,则说明之前的锁超时了,打warning log 如果oldLock.locked为true,表示锁已经被占用,加锁失败,进入轮询等待。 每3秒查询一次锁,直到锁被释放或超时 release 将lock的locked更新为false,expireAt更新为null 栗子 public static function acquire($key) { $oldLock = null; while (true) { $condition = [ 'key' => $key, '$or' => [[ 'expireAt' => null, ],[ 'expireAt' => ['$lt' => new \MongoDate()] ]] ]; $update = [ '$setOnInsert' => [ 'key' => $key, ], '$set' => [ 'locked' => true, 'expireAt' => new \MongoDate(time() + self::EXPIRE_TIME) ] ]; try { $oldLock = static::findAndModify($condition, $update, ['upsert' => true]); break; } catch (yii\mongodb\Exception $e) { sleep(self::ACQUIRE_LOCK_INTERVAL); continue; } } if (!empty($oldLock)) { if (!empty($oldLock['expireAt'])) { LogHelper::warning(__METHOD__, 'lock timeout', ['key' => $key]); while ($oldLock['locked']) { sleep(self::ACQUIRE_LOCK_INTERVAL); $oldLock = static::findAndModify(['key' => $key], $update); } } } } public static function release($key) { $condition = ['key' => $key, 'locked' => true]; $update = ['$set' => ['locked' => false, 'expireAt' => null]]; static::findAndModify($condition, $update); }