Last active
May 31, 2019 11:05
-
-
Save NHZEX/f3503760887573857c00932e5922057f to your computer and use it in GitHub Desktop.
SnowFlake 初版
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* Created by PhpStorm. | |
* User: Auoor | |
* Date: 2019/3/14 | |
* Time: 20:41 | |
*/ | |
declare(strict_types=1); | |
namespace SnowFlake; | |
class SnowFlake | |
{ | |
/** @var int 2019-01-01 00:00:00.000 - 1546272000000 */ | |
protected const TWEPOCH = 1546272000000; | |
// 是否已经初始化完成 | |
protected static $inited = false; | |
// 每一部分占用的位数 | |
protected static $bitTwepoch = 41; | |
protected static $bitDataCenter = 5; | |
protected static $bitMachine = 5; | |
protected static $bitSequence = 12; | |
// 归零偶数处理 | |
protected static $bitZeroRandSequence = 4; | |
protected static $maxZeroEvenSequence; | |
// 每一部分的最大值 | |
protected static $maxDataCenter; | |
protected static $maxMachine; | |
protected static $maxSequence; | |
// 每一部分向左的位移值 | |
protected static $machineLeft; | |
protected static $dataCenterLeft; | |
protected static $timestampLeft; | |
/** @var int */ | |
protected $dataCenterId = 0; | |
/** @var int */ | |
protected $machineId = 0; | |
/** @var int */ | |
protected $lastTimestamp = 0; | |
/** @var int */ | |
protected $sequence = 0; | |
/** | |
* SnowFlake constructor. | |
* @param int $dataCenterId | |
* @param int $machineId | |
* @throws \Exception | |
*/ | |
public function __construct(int $dataCenterId = 0, int $machineId = 0) | |
{ | |
if (!self::$inited) { | |
self::$maxDataCenter = -1 ^ (-1 << self::$bitDataCenter); | |
self::$maxMachine = -1 ^ (-1 << self::$bitMachine); | |
self::$maxSequence = -1 ^ (-1 << self::$bitSequence); | |
self::$machineLeft = self::$bitSequence; | |
self::$dataCenterLeft = self::$bitSequence + self::$bitMachine; | |
self::$timestampLeft = self::$bitSequence + self::$bitMachine + self::$bitDataCenter; | |
self::$maxZeroEvenSequence = self::$maxSequence >> self::$bitZeroRandSequence; | |
self::$inited = true; | |
} | |
if ($dataCenterId > self::$maxDataCenter) { | |
throw new \Exception('数据中心编号取值范围为:0-' . self::$maxDataCenter); | |
} | |
if ($machineId > self::$maxMachine) { | |
throw new \Exception('机器编号编号取值范围为:0-' . self::$maxMachine); | |
} | |
$this->dataCenterId = $dataCenterId; | |
$this->machineId = $machineId; | |
$this->nextId(); | |
} | |
/** | |
* 获取一个十六进制id | |
* @return string | |
*/ | |
public function nextHex(): string | |
{ | |
return dechex($this->nextId()); | |
} | |
/** | |
* 获取一个id | |
* @return int | |
*/ | |
public function nextId(): int | |
{ | |
$currStmp = $this->getCurrUnixMicroTimestamp(); | |
// TODO 允许时间回退容差 | |
if ($currStmp < $this->lastTimestamp) { | |
throw new \RuntimeException("Clock moved backwards. Refusing to generate id"); | |
} | |
if ($currStmp == $this->lastTimestamp) { | |
//相同毫秒内,序列号自增 | |
$this->sequence = ($this->sequence + 1) & self::$maxSequence; | |
//同一毫秒的序列数已经达到最大 | |
if ($this->sequence == 0) { | |
$currStmp = $this->getNextUnixMicroTimestamp(); | |
} | |
} else { | |
//不同毫秒内,序列号置为0 | |
$this->sequence = mt_rand(0, self::$maxZeroEvenSequence); | |
} | |
$this->lastTimestamp = $currStmp; | |
return $currStmp << self::$timestampLeft //时间戳部分 | |
| $this->dataCenterId << self::$dataCenterLeft //数据中心部分 | |
| $this->machineId << self::$machineLeft //机器标识部分 | |
| $this->sequence; //序列号部分 | |
} | |
/** | |
* 获取下一个毫秒时间戳 (阻塞到下一个毫秒,直到获得新的时间戳) | |
* @return int | |
*/ | |
protected function getNextUnixMicroTimestamp(): int | |
{ | |
$numt = $this->getCurrUnixMicroTimestamp(); | |
while ($numt <= $this->lastTimestamp) { | |
$numt = $this->getCurrUnixMicroTimestamp(); | |
} | |
return $numt; | |
} | |
/** | |
* 获取当前毫秒时间戳 | |
* @return int | |
*/ | |
protected function getCurrUnixMicroTimestamp(): int | |
{ | |
return (int)(microtime(true) * 1000) - self::TWEPOCH; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
declare(strict_types=1); | |
namespace HZEX\SimpleRpc; | |
use Co; | |
use LengthException; | |
use RuntimeException; | |
final class SnowFlake | |
{ | |
/** @var int 2019-01-01 00:00:00.000 - 1546272000000 */ | |
protected const TWEPOCH = 1546272000000; | |
// 是否已经初始化完成 | |
protected static $inited = false; | |
// 累加器归零处理 | |
protected static $maxSequenceZeroRand = 256; | |
// 每一部分占用的位数 | |
protected static $bitTwepoch = 41; // 毫秒级时间 | |
protected static $bitWorkerId = 8; // 工人ID | |
protected static $bitExtension = 3; // 扩展位 | |
protected static $bitSequence = 11; // 毫秒累加器 | |
// 每一部分的最大值 | |
protected static $maxExtension; | |
protected static $maxWorkerId; | |
protected static $maxSequence; | |
// 每一部分向左的位移值 | |
protected static $extensionLeft; | |
protected static $workerIdLeft; | |
protected static $timestampLeft; | |
/** @var int */ | |
protected $workerId = 0; | |
/** @var int */ | |
protected $extension = 0; | |
/** @var int */ | |
protected $lastTimestamp = 0; | |
/** @var int */ | |
protected $sequence = 0; | |
/** | |
* SnowFlake constructor. | |
* @param int $workerId | |
* @throws LengthException | |
*/ | |
public function __construct(int $workerId = 0) | |
{ | |
if (!self::$inited) { | |
self::$maxWorkerId = -1 ^ (-1 << self::$bitWorkerId); | |
self::$maxExtension = -1 ^ (-1 << self::$bitExtension); | |
self::$maxSequence = -1 ^ (-1 << self::$bitSequence); | |
self::$timestampLeft = self::$bitWorkerId + self::$bitExtension + self::$bitSequence; | |
self::$workerIdLeft = self::$bitExtension + self::$bitSequence; | |
self::$extensionLeft = self::$bitSequence; | |
self::$inited = true; | |
} | |
if ($workerId > self::$maxWorkerId) { | |
throw new LengthException('机器编号编号取值范围为:0-' . self::$maxWorkerId); | |
} | |
$this->workerId = $workerId; | |
$this->nextId(); | |
} | |
/** | |
* 获取一个十六进制id | |
* @return string | |
*/ | |
public function nextHex(): string | |
{ | |
return dechex($this->nextId()); | |
} | |
/** | |
* 获取一个id | |
* @return int | |
*/ | |
public function nextId(): int | |
{ | |
$currStmp = $this->getCurrUnixMicroTimestamp(); | |
// 时间回拨,抛出异常 TODO 允许时间回退容差 | |
if ($currStmp < $this->lastTimestamp) { | |
if (null === ($currStmp = $this->waitTimeGoBack($currStmp))) { | |
throw new RuntimeException("Clock moved backwards. Refusing to generate id"); | |
} | |
} | |
if ($currStmp == $this->lastTimestamp) { | |
//相同毫秒内,序列号自增 | |
$this->sequence = ($this->sequence + 1) & self::$maxSequence; | |
//同一毫秒的序列数已经达到最大 | |
if ($this->sequence == 0) { | |
$currStmp = $this->getNextUnixMicroTimestamp(); | |
} | |
} else { | |
//不同毫秒内,序列号重置 | |
$this->sequence = mt_rand(0, self::$maxSequenceZeroRand); | |
} | |
$this->lastTimestamp = $currStmp; | |
return $currStmp << self::$timestampLeft //时间戳部分 | |
| $this->extension << self::$extensionLeft //扩展部分 | |
| $this->workerId << self::$workerIdLeft //工人标识部分 | |
| $this->sequence; //序列号部分 | |
} | |
/** | |
* 等待时间回拨 | |
* 支持3毫秒波动纠正 | |
* @param int $microTimestamp | |
* @param int $retry | |
* @return int|null | |
*/ | |
private function waitTimeGoBack(int $microTimestamp, int $retry = 3): ?int | |
{ | |
if ($retry < 0) { | |
return null; | |
} | |
$timeDiff = $this->lastTimestamp - $microTimestamp; | |
if ($timeDiff <= 3000) { | |
if (-1 === Co::getPcid()) { | |
usleep($timeDiff); | |
} else { | |
Co::sleep(min(round($timeDiff / 1000, 2), 0.01)); | |
} | |
} | |
$microTimestamp = $this->getCurrUnixMicroTimestamp(); | |
if ($microTimestamp >= $this->lastTimestamp) { | |
return $microTimestamp; | |
} | |
$this->waitTimeGoBack($microTimestamp, --$retry); | |
return null; | |
} | |
/** | |
* 获取下一个毫秒时间戳 (阻塞到下一个毫秒,直到获得新的时间戳) | |
* @return int | |
*/ | |
protected function getNextUnixMicroTimestamp(): int | |
{ | |
$numt = $this->getCurrUnixMicroTimestamp(); | |
while ($numt <= $this->lastTimestamp) { | |
$numt = $this->getCurrUnixMicroTimestamp(); | |
} | |
return $numt; | |
} | |
/** | |
* 获取当前毫秒时间戳 | |
* @return int | |
*/ | |
protected function getCurrUnixMicroTimestamp(): int | |
{ | |
return (int)(microtime(true) * 1000) - self::TWEPOCH; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment