diff --git a/application/common/library/Snowflake.php b/application/common/library/Snowflake.php index 951b3ef5..3bbe46a2 100644 --- a/application/common/library/Snowflake.php +++ b/application/common/library/Snowflake.php @@ -1,69 +1,109 @@ self::MAX_MACHINE_ID) { - throw new \InvalidArgumentException("机器ID必须在0-" . self::MAX_MACHINE_ID . "之间"); - } - - $this->machineId = $machineId; + $this->workerId = $this->generateWorkerId(); } /** - * 生成ID - * @return string + * 获取单例实例 */ - public function generateId() + public static function getInstance() { - $timestamp = $this->getTimestamp(); + if (self::$instance === null) { + self::$instance = new self(); + } + return self::$instance; + } - if ($timestamp < $this->lastTimestamp) { - throw new \Exception("时钟回拨异常"); + /** + * 生成工作机器ID(基于服务器IP) + */ + protected function generateWorkerId() + { + // 方法1:从配置文件中读取 + if (config('snowflake.worker_id')) { + return config('snowflake.worker_id') & self::MAX_WORKER_ID; } - if ($timestamp == $this->lastTimestamp) { - $this->sequence = ($this->sequence + 1) & self::MAX_SEQUENCE; + // 方法2:基于服务器IP生成(推荐) + $serverIp = $_SERVER['SERVER_ADDR'] ?? '127.0.0.1'; + $ipParts = explode('.', $serverIp); + + // 使用IP后两段生成workerId + if (count($ipParts) >= 4) { + $workerId = ($ipParts[2] << 8) | $ipParts[3]; + } else { + // 如果是IPv6或特殊情况,使用随机数 + $workerId = mt_rand(0, self::MAX_WORKER_ID); + } + + return $workerId & self::MAX_WORKER_ID; + } + + /** + * 生成下一个ID + */ + public function nextId() + { + $timestamp = $this->getCurrentTimestamp(); + + // 时钟回拨处理 + if ($timestamp < $this->lastTimestamp) { + $diff = $this->lastTimestamp - $timestamp; + throw new \Exception("时钟回拨了 {$diff} 毫秒"); + } + + // 同一毫秒内生成多个ID + if ($this->lastTimestamp == $timestamp) { + $this->sequence = ($this->sequence + 1) & self::SEQUENCE_MASK; if ($this->sequence == 0) { + // 序列号用尽,等待下一毫秒 $timestamp = $this->waitNextMillis($this->lastTimestamp); } } else { @@ -72,51 +112,58 @@ class Snowflake $this->lastTimestamp = $timestamp; - return (($timestamp - self::EPOCH) << self::TIMESTAMP_SHIFT) | - ($this->machineId << self::MACHINE_SHIFT) | - $this->sequence; + // 组合生成ID + $id = (($timestamp - self::EPOCH) << self::TIMESTAMP_LEFT_SHIFT) + | ($this->workerId << self::WORKER_ID_SHIFT) + | $this->sequence; + + return (string)$id; + } + + /** + * 批量生成ID + */ + public function nextIds($count) + { + $ids = []; + for ($i = 0; $i < $count; $i++) { + $ids[] = $this->nextId(); + } + return $ids; } /** * 获取当前毫秒时间戳 - * @return int */ - private function getTimestamp() + protected function getCurrentTimestamp() { return (int)(microtime(true) * 1000); } /** - * 等待下一毫秒 - * @param int $lastTimestamp - * @return int + * 等待到下一毫秒 */ - private function waitNextMillis($lastTimestamp) + protected function waitNextMillis($lastTimestamp) { - $timestamp = $this->getTimestamp(); + $timestamp = $this->getCurrentTimestamp(); while ($timestamp <= $lastTimestamp) { usleep(100); // 休眠100微秒 - $timestamp = $this->getTimestamp(); + $timestamp = $this->getCurrentTimestamp(); } return $timestamp; } /** - * 解析ID - * @param string $id - * @return array + * 解析雪花ID */ - public static function parseId($id) + public static function parse($id) { - $timestamp = ($id >> self::TIMESTAMP_SHIFT) + self::EPOCH; - $machineId = ($id >> self::MACHINE_SHIFT) & self::MAX_MACHINE_ID; - $sequence = $id & self::MAX_SEQUENCE; - + $id = intval($id); return [ - 'timestamp' => $timestamp, - 'machine_id' => $machineId, - 'sequence' => $sequence, - 'generate_time' => date('Y-m-d H:i:s', $timestamp / 1000) + 'timestamp' => ($id >> self::TIMESTAMP_LEFT_SHIFT) + self::EPOCH, + 'worker_id' => ($id >> self::WORKER_ID_SHIFT) & self::MAX_WORKER_ID, + 'sequence' => $id & self::SEQUENCE_MASK, + 'generated_at' => date('Y-m-d H:i:s.v', (($id >> self::TIMESTAMP_LEFT_SHIFT) + self::EPOCH) / 1000) ]; } } \ No newline at end of file