Snowflake.php 2.5 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677
  1. <?php
  2. namespace fast;
  3. class Snowflake
  4. {
  5. private int $datacenterId; // 数据中心ID
  6. private int $machineId; // 机器ID
  7. private int $sequence = 0; // 序列号
  8. private int $lastTimestamp = -1; // 上一个时间戳
  9. private const DATA_CENTER_ID_BITS = 5; // 数据中心ID所占位数
  10. private const MACHINE_ID_BITS = 5; // 机器ID所占位数
  11. private const SEQUENCE_BITS = 12; // 序列号所占位数
  12. private const MAX_DATA_CENTER_ID = -1 ^ (-1 << self::DATA_CENTER_ID_BITS);
  13. private const MAX_MACHINE_ID = -1 ^ (-1 << self::MACHINE_ID_BITS);
  14. private const TIMESTAMP_LEFT_SHIFT = self::SEQUENCE_BITS + self::MACHINE_ID_BITS + self::DATA_CENTER_ID_BITS;
  15. private const SEQUENCE_LEFT_SHIFT = self::MACHINE_ID_BITS + self::DATA_CENTER_ID_BITS;
  16. private int $epoch; // 起始时间戳
  17. public function __construct(int $datacenterId, int $machineId)
  18. {
  19. if ($datacenterId < 0 || $datacenterId > self::MAX_DATA_CENTER_ID) {
  20. throw new \Exception("数据中心ID超出范围");
  21. }
  22. if ($machineId < 0 || $machineId > self::MAX_MACHINE_ID) {
  23. throw new \Exception("机器ID超出范围");
  24. }
  25. $this->datacenterId = $datacenterId;
  26. $this->machineId = $machineId;
  27. $this->epoch = 1609430400000; // 自定义起始时间(例如2021年1月1日)
  28. }
  29. public function nextId(): int
  30. {
  31. $timestamp = $this->currentTimeMillis();
  32. if ($timestamp < $this->lastTimestamp) {
  33. throw new \Exception("错误:系统时钟发生回拨");
  34. }
  35. if ($this->lastTimestamp === $timestamp) {
  36. $this->sequence = ($this->sequence + 1) & ((1 << self::SEQUENCE_BITS) - 1);
  37. if ($this->sequence === 0) {
  38. $timestamp = $this->waitNextMillis($timestamp);
  39. }
  40. } else {
  41. $this->sequence = 0;
  42. }
  43. $this->lastTimestamp = $timestamp;
  44. return (($timestamp - $this->epoch) << self::TIMESTAMP_LEFT_SHIFT) |
  45. ($this->datacenterId << self::SEQUENCE_LEFT_SHIFT) |
  46. ($this->machineId << self::SEQUENCE_BITS) |
  47. $this->sequence;
  48. }
  49. private function waitNextMillis(int $lastTimestamp): int
  50. {
  51. $timestamp = $this->currentTimeMillis();
  52. while ($timestamp <= $lastTimestamp) {
  53. $timestamp = $this->currentTimeMillis();
  54. }
  55. return $timestamp;
  56. }
  57. private function currentTimeMillis(): int
  58. {
  59. return (int)(microtime(true) * 1000);
  60. }
  61. }