87 lines
2.0 KiB
PHP
87 lines
2.0 KiB
PHP
<?php
|
|
|
|
namespace app\utils;
|
|
|
|
/**
|
|
* 雪花算法 - 分布式唯一ID生成器
|
|
* Class Snowflake
|
|
* @package app\utils
|
|
*/
|
|
class Snowflake
|
|
{
|
|
private const EPOCH = 1704067200000; // 2024-01-01 00:00:00 UTC
|
|
private const WORKER_ID_BITS = 5;
|
|
private const DATACENTER_ID_BITS = 5;
|
|
private const SEQUENCE_BITS = 12;
|
|
|
|
private $workerId;
|
|
private $datacenterId;
|
|
private $sequence = 0;
|
|
private $lastTimestamp = -1;
|
|
|
|
private static $instance = null;
|
|
|
|
public function __construct($workerId = 1, $datacenterId = 1)
|
|
{
|
|
$this->workerId = $workerId & 0x1F;
|
|
$this->datacenterId = $datacenterId & 0x1F;
|
|
}
|
|
|
|
/**
|
|
* 获取单例实例
|
|
*/
|
|
public static function getInstance()
|
|
{
|
|
if (self::$instance === null) {
|
|
self::$instance = new self(1, 1);
|
|
}
|
|
return self::$instance;
|
|
}
|
|
|
|
/**
|
|
* 生成下一个ID
|
|
*/
|
|
public function nextId()
|
|
{
|
|
$timestamp = $this->currentTimeMillis();
|
|
|
|
if ($timestamp === $this->lastTimestamp) {
|
|
$this->sequence = ($this->sequence + 1) & 0xFFF;
|
|
if ($this->sequence === 0) {
|
|
$timestamp = $this->waitNextMillis($this->lastTimestamp);
|
|
}
|
|
} else {
|
|
$this->sequence = 0;
|
|
}
|
|
|
|
$this->lastTimestamp = $timestamp;
|
|
|
|
return (($timestamp - self::EPOCH) << 22)
|
|
| ($this->datacenterId << 17)
|
|
| ($this->workerId << 12)
|
|
| $this->sequence;
|
|
}
|
|
|
|
/**
|
|
* 生成字符串ID
|
|
*/
|
|
public function nextIdString()
|
|
{
|
|
return (string)$this->nextId();
|
|
}
|
|
|
|
private function currentTimeMillis()
|
|
{
|
|
return (int)(microtime(true) * 1000);
|
|
}
|
|
|
|
private function waitNextMillis($lastTimestamp)
|
|
{
|
|
$timestamp = $this->currentTimeMillis();
|
|
while ($timestamp <= $lastTimestamp) {
|
|
$timestamp = $this->currentTimeMillis();
|
|
}
|
|
return $timestamp;
|
|
}
|
|
}
|