ensureRedis()) { $key = self::KEY_PREFIX . $splitLinkId; $seq = (int) $this->redis->incr($key); if ($seq <= 0) { $seq = 1; } return ($seq - 1) % $numberCount; } return $this->nextIndexFallback($splitLinkId, $numberCount); } /** * 尝试连接 Redis(配置来自 queue 扩展) */ private function ensureRedis(): bool { if ($this->redisAttempted) { return $this->redisReady; } $this->redisAttempted = true; if (!extension_loaded('redis')) { return false; } $config = Config::get('queue'); if (!is_array($config) || ($config['connector'] ?? '') !== 'Redis') { $appPath = defined('APP_PATH') ? APP_PATH : (defined('ROOT_PATH') ? ROOT_PATH . 'application/' : ''); $file = $appPath . 'extra/queue.php'; if (is_file($file)) { $loaded = include $file; $config = is_array($loaded) ? $loaded : []; } else { $config = []; } } $host = (string) ($config['host'] ?? '127.0.0.1'); $port = (int) ($config['port'] ?? 6379); $password = (string) ($config['password'] ?? ''); $select = (int) ($config['select'] ?? 0); $timeout = (float) ($config['timeout'] ?? 1.0); try { $redis = new \Redis(); $connected = $timeout > 0 ? @$redis->connect($host, $port, $timeout) : @$redis->connect($host, $port); if (!$connected) { return false; } if ($password !== '') { if (!$redis->auth($password)) { return false; } } if ($select > 0) { $redis->select($select); } $this->redis = $redis; $this->redisReady = true; } catch (\Throwable $e) { $this->redisReady = false; } return $this->redisReady; } /** * 无 Redis 时使用 runtime 文件锁递增(开发/单机可用;生产请启用 Redis) */ private function nextIndexFallback(int $splitLinkId, int $numberCount): int { $runtime = defined('RUNTIME_PATH') ? RUNTIME_PATH : (dirname(__DIR__, 3) . '/runtime/'); $dir = $runtime . 'split_rr/'; if (!is_dir($dir) && !@mkdir($dir, 0755, true)) { return 0; } $file = $dir . $splitLinkId . '.cnt'; $fp = @fopen($file, 'c+'); if ($fp === false) { return 0; } if (!flock($fp, LOCK_EX)) { fclose($fp); return 0; } $raw = stream_get_contents($fp); $seq = (int) $raw; $seq++; ftruncate($fp, 0); rewind($fp); fwrite($fp, (string) $seq); fflush($fp); flock($fp, LOCK_UN); fclose($fp); return ($seq - 1) % $numberCount; } }