key = "uid_rotator:{$rotatorName}:index"; $this->setKey = "uid_rotator:{$rotatorName}:uids"; $this->lockKey = "uid_rotator:{$rotatorName}:lock"; $this->expireSeconds = $expireSeconds; } public function setUids(array $uids, bool $resetIndex = true): bool { // 删除旧数据 Redis::del($this->setKey); if (!empty($uids)) { // 添加新UID集合 foreach ($uids as $uid) { Redis::sAdd($this->setKey, $uid); } // 重置索引 if ($resetIndex) { Redis::set($this->key, 0); } } $this->touchExpire(); return true; } public function next(): ?int { $lockAcquired = false; try { // 获取锁(改用原生命令) $lockAcquired = $this->acquireLock(5); if (!$lockAcquired) { throw new \RuntimeException("获取锁失败"); } // 获取当前索引 $currentIndex = (int)Redis::get($this->key); // 获取所有UID $uids = Redis::sMembers($this->setKey); if (empty($uids)) { return null; } // 计算位置 $uid = $uids[$currentIndex % count($uids)]; $nextIndex = $currentIndex + 1; // 更新索引 Redis::set($this->key, $nextIndex); $this->touchExpire(); return is_numeric($uid) ? (int)$uid : $uid; } catch (\Throwable $e) { Log::info("UID分配错误: " . $e->getMessage()); return null; } finally { if ($lockAcquired) { Redis::del($this->lockKey); } } } private function acquireLock(int $timeout = 5): bool { $start = time(); $lockTimeout = $timeout + 1; // 锁过期时间比等待时间长 while (time() - $start < $timeout) { // 使用SETNX实现锁(分两步) if (Redis::setNx($this->lockKey, 1)) { // 设置过期时间 Redis::expire($this->lockKey, $lockTimeout); return true; } // 检查锁是否已过期但未被删除(处理死锁) if (Redis::ttl($this->lockKey) < 0) { Redis::del($this->lockKey); } usleep(100000); // 100ms重试间隔 } return false; } private function touchExpire(): void { Redis::expire($this->key, $this->expireSeconds); Redis::expire($this->setKey, $this->expireSeconds); } public function getPosition(): int { return (int)Redis::get($this->key); } public function resetIndex(int $position = 0): bool { return (bool)Redis::set($this->key, max(0, $position)); } public function getUids(): array { return Redis::sMembers($this->setKey) ?: []; } public function addUids($uids): int { $added = 0; $uids = is_array($uids) ? $uids : [$uids]; foreach ($uids as $uid) { $added += Redis::sAdd($this->setKey, $uid); } $this->touchExpire(); return $added; } public function removeUids($uids): int { $removed = 0; $uids = is_array($uids) ? $uids : [$uids]; foreach ($uids as $uid) { $removed += Redis::sRem($this->setKey, $uid); } return $removed; } public function count(): int { return (int)Redis::sCard($this->setKey); } public function destroy(): void { Redis::del([$this->key, $this->setKey, $this->lockKey]); } }