├── .gitignore ├── src └── Snowflake │ ├── Server │ ├── .gitignore │ ├── CountServerInterFace.php │ ├── RedisCountServer.php │ └── FileCountServer.php │ └── IdWorker.php ├── composer.json ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | /vendor/ 3 | -------------------------------------------------------------------------------- /src/Snowflake/Server/.gitignore: -------------------------------------------------------------------------------- 1 | sequenceId.lock -------------------------------------------------------------------------------- /src/Snowflake/Server/CountServerInterFace.php: -------------------------------------------------------------------------------- 1 | =7.0.0" 18 | }, 19 | "autoload": { 20 | "psr-4": { 21 | "wantp\\Snowflake\\": "src/Snowflake/" 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 zhang rongwang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/Snowflake/Server/RedisCountServer.php: -------------------------------------------------------------------------------- 1 | redis = new \Redis(); 18 | if (!isset($config['host']) || !isset($config['port'])) { 19 | throw new \Exception('invalid redis config'); 20 | } 21 | $this->redis->connect($config['host'], $config['port']); 22 | if (isset($config['auth'])) { 23 | $this->redis->auth($config['auth']); 24 | } 25 | if (isset($config['dbIndex'])) { 26 | $this->redis->select($config['dbIndex']); 27 | } 28 | return $this; 29 | } 30 | 31 | /** 32 | * Notes:getSequenceId 33 | * @author zhangrongwang 34 | * @date 2018-12-26 10:42:20 35 | * @param $key 36 | * @return int $sequenceId 37 | */ 38 | public function getSequenceId($key) 39 | { 40 | $sequenceId = $this->redis->incr($key) - 1; 41 | $this->redis->expire($key, 5); 42 | return $sequenceId; 43 | } 44 | } -------------------------------------------------------------------------------- /src/Snowflake/Server/FileCountServer.php: -------------------------------------------------------------------------------- 1 | 1bit正负标识位 - 41bits毫秒级时间戳 - 5bits数据中心id - 5bits机器id -12bits毫秒内顺序id 17 | 18 | ##### 使用说明 19 | 20 | ###### 引用包 21 | ``` 22 | composer require wantp/snowflake 23 | ``` 24 | ###### 生成id 25 | ``` 26 | require_once 'vendor/autoload.php'; 27 | 28 | $IdWorker = \wantp\Snowflake\IdWorker::getIns(); 29 | $id = $IdWorker->id(); 30 | 31 | ``` 32 | 33 | ###### 反向解析id 34 | 35 | ``` 36 | $idInfo = $IdWorker->parse($id); 37 | ``` 38 | 39 | ###### 分布式,设置机器id 40 | 41 | ``` 42 | $dataCenterId = 2; 43 | $machineId = 5; 44 | $IdWorker = \wantp\Snowflake\IdWorker::getIns($dataCenterId,$machineId); 45 | $id = $IdWorker->id(); 46 | ``` 47 | 48 | ###### 使用redis来控制并发 49 | - 需要先安装配置好redis,设置redis时需要填写redis配置 50 | - redis配置说明 51 | 52 | | key | 是否必填 | 说明 | 53 | |:----:|:---:|:---:| 54 | |host|是|redis主机| 55 | |port|是|redis端口| 56 | |dbIndex|否|redis db index| 57 | |auth|否|redis认证| 58 | 59 | ``` 60 | $resdisConfig = ['host'=>'redis host','port'=>'redis port','dbIndex'=>'redis dbIndex',auth'=>'redis auth']; 61 | $IdWorker = \wantp\Snowflake\IdWorker::getIns()->setRedisConutServer($resdisConfig); 62 | $id = $IdWorker->id(); 63 | ``` -------------------------------------------------------------------------------- /src/Snowflake/IdWorker.php: -------------------------------------------------------------------------------- 1 | $this->maxDataCenterId) { 70 | throw new \Exception('data center id should between 0 and ' . $this->maxDataCenterId); 71 | } 72 | if ($machine_id > $this->maxMachineId) { 73 | throw new \Exception('machine id should between 0 and ' . $this->maxMachineId); 74 | } 75 | $this->dataCenterId = $dataCenter_id; 76 | $this->machineId = $machine_id; 77 | $this->countService = new FileCountServer(); 78 | } 79 | 80 | /** 81 | * Notes:__clone 82 | * @author zhangrongwang 83 | * @date 2018-12-25 11:42:06 84 | */ 85 | private function __clone() 86 | { 87 | 88 | } 89 | 90 | /** 91 | * Notes:getIns 92 | * @author zhangrongwang 93 | * @date 2018-12-25 11:42:06 94 | * @param int $dataCenterId 95 | * @param int $machineId 96 | * @throws \Exception 97 | * @return IdWorker $snowflake; 98 | */ 99 | public static function getIns($dataCenterId = 0, $machineId = 0) 100 | { 101 | if (!(self::$idWorker instanceof self)) { 102 | self::$idWorker = new self($dataCenterId, $machineId); 103 | } 104 | return self::$idWorker; 105 | } 106 | 107 | /** 108 | * Notes:setFileCountServer 109 | * @author zhangrongwang 110 | * @date 2018-12-26 10:40:58 111 | * @return IdWorker 112 | */ 113 | public function setFileCountServer() 114 | { 115 | $this->countService = new FileCountServer(); 116 | return $this; 117 | } 118 | 119 | /** 120 | * Notes:setRedisCountServer 121 | * @author zhangrongwang 122 | * @date 2018-12-26 10:41:02 123 | * @param $config ['host'=>'','port'=>'','dbIndex'=>'','auth'=>''] 124 | * @throws \Exception 125 | * @return IdWorker 126 | */ 127 | public function setRedisCountServer($config) 128 | { 129 | $this->countService = new RedisCountServer($config); 130 | return $this; 131 | } 132 | 133 | /** 134 | * Notes:id 135 | * @author zhangrongwang 136 | * @date 2018-12-25 11:48:09 137 | * @throws \Exception 138 | */ 139 | public function id() 140 | { 141 | $sign = 0; 142 | $timestamp = $this->getUnixTimestamp(); 143 | if ($timestamp < $this->lastTimestamp) { 144 | throw new \Exception('Clock moved backwards!'); 145 | } 146 | $countServiceKey = $this->dataCenterId . '-' . $this->machineId . '-' . $timestamp; 147 | $sequence = $this->countService->getSequenceId($countServiceKey); 148 | if ($sequence > $this->maxSequenceId) { 149 | $timestamp = $this->getUnixTimestamp(); 150 | while ($timestamp <= $this->lastTimestamp) { 151 | $timestamp = $this->getUnixTimestamp(); 152 | } 153 | $countServiceKey = $this->dataCenterId . '-' . $this->machineId . '-' . $timestamp; 154 | $sequence = $this->countService->getSequenceId($countServiceKey); 155 | } 156 | $this->lastTimestamp = $timestamp; 157 | $time = (int)($timestamp - self::EPOCH_OFFSET); 158 | $id = ($sign << $this->signLeftShift) | ($time << $this->timestampLeftShift) | ($this->dataCenterId << $this->dataCenterLeftShift) | ($this->machineId << $this->machineLeftShift) | $sequence; 159 | return (string)$id; 160 | } 161 | 162 | /** 163 | * Notes:parse 164 | * @author zhangrongwang 165 | * @date 2018-12-25 16:41:27 166 | * @param $uuid 167 | * @return array 168 | */ 169 | public function parse($uuid) 170 | { 171 | $binUuid = decbin($uuid); 172 | $len = strlen($binUuid); 173 | $sequenceStart = $len - self::SEQUENCE_BITS; 174 | $sequence = substr($binUuid, $sequenceStart, self::SEQUENCE_BITS); 175 | $machineIdStart = $len - self::MACHINE_ID_BITS - self::SEQUENCE_BITS; 176 | $machineId = substr($binUuid, $machineIdStart, self::MACHINE_ID_BITS); 177 | $dataCenterIdStart = $len - self::DATA_CENTER_BITS - self::MACHINE_ID_BITS - self::SEQUENCE_BITS; 178 | $dataCenterId = substr($binUuid, $dataCenterIdStart, self::DATA_CENTER_BITS); 179 | $timestamp = substr($binUuid, 0, $dataCenterIdStart); 180 | $realTimestamp = bindec($timestamp) + self::EPOCH_OFFSET; 181 | $timestamp = substr($realTimestamp, 0, -3); 182 | $microSecond = substr($realTimestamp, -3); 183 | return [ 184 | 'timestamp' => date('Y-m-d H:i:s', $timestamp) . '.' . $microSecond, 185 | 'dataCenterId' => bindec($dataCenterId), 186 | 'machineId' => bindec($machineId), 187 | 'sequence' => bindec($sequence), 188 | ]; 189 | } 190 | 191 | /** 192 | * Notes:getUnixTimestamp 193 | * @author zhangrongwang 194 | * @date 2018-12-25 11:42:06 195 | */ 196 | private function getUnixTimestamp() 197 | { 198 | return floor(microtime(true) * 1000); 199 | } 200 | } --------------------------------------------------------------------------------