122 lines
2.9 KiB
PHP
122 lines
2.9 KiB
PHP
|
|
<?php
|
|||
|
|
namespace app\common\library;
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 雪花算法ID生成器
|
|||
|
|
*/
|
|||
|
|
class Snowflake
|
|||
|
|
{
|
|||
|
|
// 起始时间戳(2023-01-01 00:00:00)
|
|||
|
|
const EPOCH = 1672531200000;
|
|||
|
|
|
|||
|
|
// 机器ID位数
|
|||
|
|
const MACHINE_BITS = 10;
|
|||
|
|
|
|||
|
|
// 序列号位数
|
|||
|
|
const SEQUENCE_BITS = 12;
|
|||
|
|
|
|||
|
|
// 最大机器ID
|
|||
|
|
const MAX_MACHINE_ID = -1 ^ (-1 << self::MACHINE_BITS);
|
|||
|
|
|
|||
|
|
// 最大序列号
|
|||
|
|
const MAX_SEQUENCE = -1 ^ (-1 << self::SEQUENCE_BITS);
|
|||
|
|
|
|||
|
|
// 机器ID左移位数
|
|||
|
|
const MACHINE_SHIFT = self::SEQUENCE_BITS;
|
|||
|
|
|
|||
|
|
// 时间戳左移位数
|
|||
|
|
const TIMESTAMP_SHIFT = self::SEQUENCE_BITS + self::MACHINE_BITS;
|
|||
|
|
|
|||
|
|
// 机器ID(0-1023)
|
|||
|
|
private $machineId;
|
|||
|
|
|
|||
|
|
// 最后时间戳
|
|||
|
|
private $lastTimestamp = -1;
|
|||
|
|
|
|||
|
|
// 序列号
|
|||
|
|
private $sequence = 0;
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 构造函数
|
|||
|
|
* @param int $machineId 机器ID
|
|||
|
|
*/
|
|||
|
|
public function __construct($machineId = 1)
|
|||
|
|
{
|
|||
|
|
if ($machineId < 0 || $machineId > self::MAX_MACHINE_ID) {
|
|||
|
|
throw new \InvalidArgumentException("机器ID必须在0-" . self::MAX_MACHINE_ID . "之间");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$this->machineId = $machineId;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 生成ID
|
|||
|
|
* @return string
|
|||
|
|
*/
|
|||
|
|
public function generateId()
|
|||
|
|
{
|
|||
|
|
$timestamp = $this->getTimestamp();
|
|||
|
|
|
|||
|
|
if ($timestamp < $this->lastTimestamp) {
|
|||
|
|
throw new \Exception("时钟回拨异常");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if ($timestamp == $this->lastTimestamp) {
|
|||
|
|
$this->sequence = ($this->sequence + 1) & self::MAX_SEQUENCE;
|
|||
|
|
if ($this->sequence == 0) {
|
|||
|
|
$timestamp = $this->waitNextMillis($this->lastTimestamp);
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
$this->sequence = 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$this->lastTimestamp = $timestamp;
|
|||
|
|
|
|||
|
|
return (($timestamp - self::EPOCH) << self::TIMESTAMP_SHIFT) |
|
|||
|
|
($this->machineId << self::MACHINE_SHIFT) |
|
|||
|
|
$this->sequence;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 获取当前毫秒时间戳
|
|||
|
|
* @return int
|
|||
|
|
*/
|
|||
|
|
private function getTimestamp()
|
|||
|
|
{
|
|||
|
|
return (int)(microtime(true) * 1000);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 等待下一毫秒
|
|||
|
|
* @param int $lastTimestamp
|
|||
|
|
* @return int
|
|||
|
|
*/
|
|||
|
|
private function waitNextMillis($lastTimestamp)
|
|||
|
|
{
|
|||
|
|
$timestamp = $this->getTimestamp();
|
|||
|
|
while ($timestamp <= $lastTimestamp) {
|
|||
|
|
usleep(100); // 休眠100微秒
|
|||
|
|
$timestamp = $this->getTimestamp();
|
|||
|
|
}
|
|||
|
|
return $timestamp;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 解析ID
|
|||
|
|
* @param string $id
|
|||
|
|
* @return array
|
|||
|
|
*/
|
|||
|
|
public static function parseId($id)
|
|||
|
|
{
|
|||
|
|
$timestamp = ($id >> self::TIMESTAMP_SHIFT) + self::EPOCH;
|
|||
|
|
$machineId = ($id >> self::MACHINE_SHIFT) & self::MAX_MACHINE_ID;
|
|||
|
|
$sequence = $id & self::MAX_SEQUENCE;
|
|||
|
|
|
|||
|
|
return [
|
|||
|
|
'timestamp' => $timestamp,
|
|||
|
|
'machine_id' => $machineId,
|
|||
|
|
'sequence' => $sequence,
|
|||
|
|
'generate_time' => date('Y-m-d H:i:s', $timestamp / 1000)
|
|||
|
|
];
|
|||
|
|
}
|
|||
|
|
}
|