self::MAX_WORKER_ID || $workerId < 0) { throw new \Exception("worker Id can't be greater than " . self::MAX_WORKER_ID . " or less than 0"); } if ($datacenterId > self::MAX_DATACENTER_ID || $datacenterId < 0) { throw new \Exception("datacenter Id can't be greater than " . self::MAX_DATACENTER_ID . " or less than 0"); } $this->workerId = $workerId; $this->datacenterId = $datacenterId; } public function nextId() { $timestamp = $this->timeGen(); if ($timestamp < $this->lastTimestamp) { $diff = $this->lastTimestamp - $timestamp; throw new \Exception("Clock moved backwards. Refusing to generate id for {$diff} milliseconds"); } if ($this->lastTimestamp == $timestamp) { $this->sequence = ($this->sequence + 1) & self::MAX_SEQUENCE; if ($this->sequence == 0) { $timestamp = $this->tilNextMillis($this->lastTimestamp); } } else { $this->sequence = 0; } $this->lastTimestamp = $timestamp; return (($timestamp - self::EPOCH) << self::TIMESTAMP_LEFT_SHIFT) | ($this->datacenterId << self::DATACENTER_ID_SHIFT) | ($this->workerId << self::WORKER_ID_SHIFT) | $this->sequence; } protected function tilNextMillis($lastTimestamp) { $timestamp = $this->timeGen(); while ($timestamp <= $lastTimestamp) { $timestamp = $this->timeGen(); } return $timestamp; } protected function timeGen() { return floor(microtime(true) * 1000); } /** * 解析雪花ID * @param int $id * @return array */ public static function parseId($id) { $binary = decbin($id); $binary = str_pad($binary, 64, '0', STR_PAD_LEFT); $timestamp = bindec(substr($binary, 0, 42)); $timestamp = $timestamp + self::EPOCH; $datacenterId = bindec(substr($binary, 42, 5)); $workerId = bindec(substr($binary, 47, 5)); $sequence = bindec(substr($binary, 52, 12)); return [ 'timestamp' => $timestamp, 'datacenterId' => $datacenterId, 'workerId' => $workerId, 'sequence' => $sequence ]; } /** * 生成ID(单例模式) * @return int */ public static function generate() { static $instance = null; if (!$instance) { // 从配置获取workerId和datacenterId $workerId = config('snowflake.worker_id') ?: 1; $datacenterId = config('snowflake.datacenter_id') ?: 1; $instance = new self($workerId, $datacenterId); } return $instance->nextId(); } }