Merge remote-tracking branch 'origin/develop' into develop

This commit is contained in:
2025-12-28 21:51:26 +08:00

View File

@@ -1,69 +1,109 @@
<?php <?php
namespace app\common\library; namespace app\common\library;
/** /**
* 雪花算法ID生成器 * 雪花算法ID生成器
* 不依赖数据库通过配置或自动生成workerId
* 64位ID结构0(1位符号位) + 41位时间戳 + 10位工作机器ID + 12位序列号
*/ */
class Snowflake class Snowflake
{ {
// 起始时间戳2023-01-01 00:00:00 // 起始时间戳2023-01-01 00:00:00可使用到2089年
const EPOCH = 1672531200000; const EPOCH = 1672531200000;
// 机器ID位数 // 工作机器ID位数
const MACHINE_BITS = 10; const WORKER_ID_BITS = 10;
// 序列号位数 // 序列号位数
const SEQUENCE_BITS = 12; const SEQUENCE_BITS = 12;
// 最大机器ID // 最大工作机器ID
const MAX_MACHINE_ID = -1 ^ (-1 << self::MACHINE_BITS); const MAX_WORKER_ID = (1 << self::WORKER_ID_BITS) - 1;
// 最大序列号 // 序列号掩码
const MAX_SEQUENCE = -1 ^ (-1 << self::SEQUENCE_BITS); const SEQUENCE_MASK = (1 << self::SEQUENCE_BITS) - 1;
// 机器ID左移位数 // 工作机器ID左移位数
const MACHINE_SHIFT = self::SEQUENCE_BITS; const WORKER_ID_SHIFT = self::SEQUENCE_BITS;
// 时间戳左移位数 // 时间戳左移位数
const TIMESTAMP_SHIFT = self::SEQUENCE_BITS + self::MACHINE_BITS; const TIMESTAMP_LEFT_SHIFT = self::SEQUENCE_BITS + self::WORKER_ID_BITS;
// 机器ID0-1023 // 工作机器ID通过服务器IP生成
private $machineId; protected $workerId;
// 最后时间戳
private $lastTimestamp = -1;
// 序列号 // 序列号
private $sequence = 0; protected $sequence = 0;
// 上次生成ID的时间戳
protected $lastTimestamp = -1;
// 单例实例
protected static $instance = null;
/** /**
* 构造函数 * 构造函数私有化,单例模式
* @param int $machineId 机器ID
*/ */
public function __construct($machineId = 1) private function __construct()
{ {
if ($machineId < 0 || $machineId > self::MAX_MACHINE_ID) { $this->workerId = $this->generateWorkerId();
throw new \InvalidArgumentException("机器ID必须在0-" . self::MAX_MACHINE_ID . "之间");
}
$this->machineId = $machineId;
} }
/** /**
* 生成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) { // 方法2基于服务器IP生成推荐
$this->sequence = ($this->sequence + 1) & self::MAX_SEQUENCE; $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) { if ($this->sequence == 0) {
// 序列号用尽,等待下一毫秒
$timestamp = $this->waitNextMillis($this->lastTimestamp); $timestamp = $this->waitNextMillis($this->lastTimestamp);
} }
} else { } else {
@@ -72,51 +112,58 @@ class Snowflake
$this->lastTimestamp = $timestamp; $this->lastTimestamp = $timestamp;
return (($timestamp - self::EPOCH) << self::TIMESTAMP_SHIFT) | // 组合生成ID
($this->machineId << self::MACHINE_SHIFT) | $id = (($timestamp - self::EPOCH) << self::TIMESTAMP_LEFT_SHIFT)
$this->sequence; | ($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); 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) { while ($timestamp <= $lastTimestamp) {
usleep(100); // 休眠100微秒 usleep(100); // 休眠100微秒
$timestamp = $this->getTimestamp(); $timestamp = $this->getCurrentTimestamp();
} }
return $timestamp; return $timestamp;
} }
/** /**
* 解析ID * 解析雪花ID
* @param string $id
* @return array
*/ */
public static function parseId($id) public static function parse($id)
{ {
$timestamp = ($id >> self::TIMESTAMP_SHIFT) + self::EPOCH; $id = intval($id);
$machineId = ($id >> self::MACHINE_SHIFT) & self::MAX_MACHINE_ID;
$sequence = $id & self::MAX_SEQUENCE;
return [ return [
'timestamp' => $timestamp, 'timestamp' => ($id >> self::TIMESTAMP_LEFT_SHIFT) + self::EPOCH,
'machine_id' => $machineId, 'worker_id' => ($id >> self::WORKER_ID_SHIFT) & self::MAX_WORKER_ID,
'sequence' => $sequence, 'sequence' => $id & self::SEQUENCE_MASK,
'generate_time' => date('Y-m-d H:i:s', $timestamp / 1000) 'generated_at' => date('Y-m-d H:i:s.v', (($id >> self::TIMESTAMP_LEFT_SHIFT) + self::EPOCH) / 1000)
]; ];
} }
} }