当前位置: 代码迷 >> 综合 >> php 生成雪花id数组,自定义生成id数组,生成uuid
  详细解决方案

php 生成雪花id数组,自定义生成id数组,生成uuid

热度:69   发布时间:2023-10-18 08:49:27.0

秒杀订单需要提前生成订单号时,可以使用雪花id来作为订单号;

如果对订单号有要求,可以使用alpha_id,自定义数据的长度;然后自己进行拼接其他的数字长度【自定义拼接】

至于uuid:目前还没有需求- 一般用于软件注册码之类的

基于thinkphp的容器方式返回数据:里面的app('api_result')是接口的统一处理返回值类app('api_log')是日志类 app('alarm') 是邮件通知类 ,所以请参考方式即可

<?php
/** @Description: 生成唯一ID * 雪花算法是由 1 + 41 + 5 + 5 + 12 = 64 位二进制组成的,最终将这些二进制转化为 10 进制得到雪花 ID* 第一个 bit 为未使用的符号位。* 第二部分由 41 位的时间戳(毫秒)构成,他的取值是当前时间相对于某一时间的偏移量。* 第三部分和第四部分的 5 个 bit 位表示数据中心和机器ID,其能表示的最大值为 2^5 -1 = 31。* 最后部分由 12 个 bit 组成,其表示每个工作节点每毫秒生成的序列号 ID,同一毫秒内最多可生成 2^12 -1 即 4095 个 ID。* @ComposerRequire: 雪花算法:composer require godruoyi/php-snowflake * UUID:composer require ramsey/uuid 3.9.3  * UUID依赖包:composer require paragonie/random_compat 2.0.9 * UUID依赖包:composer require symfony/polyfill-ctype 1.19.0* @ComposerRemove: 雪花算法:composer remove godruoyi/php-snowflake * UUID:composer remove ramsey/uuid >> composer remove paragonie/random_compat >> composer remove symfony/polyfill-ctype* @Author: 阿海 <764882431@qq.com>* @Date: 2021-02-01 17:10:06* @LastEditTime: 2021-02-22 11:50:03* @LastEditors: 阿海*/
namespace app\common\library;use Godruoyi\Snowflake\Snowflake;
use Ramsey\Uuid\Uuid;
// use think\Exception;class Uniqid {/*** 雪花算法 取值0:则随机(1,31)* 数据中心 取值范围1-31 最大值为:2^5-1=31*/private $datacenterId = 1;/*** 雪花算法 取值0:则随机(1,31)* 机器id 取值范围1-31 最大值为:2^5-1=31*/private $workerId = 1;/*** 雪花算法 === 41 个 1 组成的二进制转为 10 进制得到时间差的毫秒数,转化为 年 为 69年* 41 位的二进制长度最多能表示 2^41 -1 毫秒即69 年,所以雪花算法最多能正常使用 69年,为了能最大限度的使用该算法,应该为其指定一个开始时间*/private $startTime = 1614049994*1000; //2021-01-30的时间戳*1000毫秒public function __construct($config=[]){$this->datacenterId = isset($config['datacenterId'])?(int)$config['datacenterId']:$this->datacenterId;$this->workerId = isset($config['workerId'])?(int)$config['workerId']:$this->workerId;$this->startTime = isset($config['startTime'])?(int)$config['startTime']:$this->startTime;}/*** @param int $number 新创建雪花id的个数* @param array $data 已经存在的id数组* 雪花算法* 时钟错乱:基于redis* ==》 验证当前系统(eg:生成一个lock文件) 是否注册过机器id * ==》(没有则注册机器id)校验系统当前时间是否大于上次上报的时间(失败时启动失败报警)* ==》 如果是分布式系统,则校验各个系统时间,取平均值校验(失败时启动失败报警)* ==》 周期性上报系统时间【redis记录】* ==》 开始批量创建 雪花id* 生成id -- 前端js可能会丢失精度,需要转为string*/public function id ( $number = 1, $data=[]){try{//注册机器id$res = $this->checkIdRegister();if($res['code'] !== 0){return app('api_result')->returnArray($res['code']);}//验证系统时间$res = $this->checkSystemTime();if($res['code'] !== 0){return app('api_result')->returnArray($res['code'],['msg'=>$res['msg']]);}//开始批量创建雪花id$snowflake = new Snowflake($this->datacenterId, $this->workerId);$snowflake->setStartTimeStamp($this->startTime);//叠加雪花IDfor($i=0; $i<$number; $i++){$data[] =  (string) $snowflake->id();}//去重过滤$data = array_unique($data);//数据不够,继续写入if(count($data)<$number){return $this->id($number-count(array_unique($data)),$data);}$data['msg'] = '已创建:'.count($data)."个雪花id;机器id:".$this->workerId;app('api_log',['config'=>['type'=>'file']])->tag("snowflake")->write($data['msg'],'info','file');return app('api_result')->returnArray(0,$data);}catch(\Exception $e){$msg = "文件:" . $e->getFile() .";代码不可执行,错误行:".$e->getLine().";错误信息:".$e->getMessage();app('api_log',['config'=>['type'=>'file']])->tag("uniqid")->write($msg,'error','file');app('alarm')->alarm($msg);return app('api_result')->echoJson(3,['msg'=>$msg]);}}/*** 雪花算法:验证 机器id * 1.是否已注册 机器id, 返回机器id* 2.是否以注册满了==>(满了,启动失败报警)未满,返回新的机器id */public function checkIdRegister(){try{//由于目前没有redis,使用session来做测试!$workerIds = !empty(session('workerIds','','register'))?session('workerIds','','register'):[];//已经注册了机器码=返回机器idif(isset($workerIds[md5(request()->ip())])){$this->workerId = $workerIds[md5(request()->ip())]['workerId'];return app("api_result")->returnArray(0,['msg'=>'机器码已注册绑定,重新赋予当前机器码']);}//判断是否已经满了,未满则分配机器id,否则触发失败报警if(count($workerIds)<31){$workerIds[md5(request()->ip())] = ['workerId'=>count($workerIds)+1];session('workerIds',$workerIds,'register');$this->workerId = $workerIds[md5(request()->ip())]['workerId'];return app("api_result")->returnArray(0,['msg'=>'机器码未注册,正在注册新的机器码']);}$msg = "雪花机器id已满:".count($workerIds)."个;当前ip的md5:".md5(request()->ip()).";当前的机器码有:".json_encode($workerIds);app('api_log',['config'=>['type'=>'file']])->tag("snowflake")->write($msg,'error','file');app('alarm')->alarm($msg);return app("api_result")->returnArray('3',['msg'=>'机器id已被注册满了','err'=>$msg]);}catch(\Exception $e){$msg = "文件:" . $e->getFile() .";代码不可执行,错误行:".$e->getLine().";错误信息:".$e->getMessage();app('api_log',['config'=>['type'=>'file']])->tag("uniqid")->write($msg,'error','file');app('alarm')->alarm($msg);return app('api_result')->echoJson(3,['msg'=>$msg]);}}/*** 周期性上报系统时间,验证当前时间大于上次上报的系统时间==》验证通过,重新上报系统时间 ,验证不通过,启动失败报警*/public function checkSystemTime(){try{$systemTime = session('system_time','',md5(request()->ip()));// $systemTime = time()+1;//测试时间异常报警if(time()>$systemTime){session('system_time',time(),md5(request()->ip()));return app("api_result")->returnArray(0,['msg'=>'系统时间正常']);}else{$msg = "【系统时间错误】系统上次上报的时间是:".date("Y-m-d H:i:s",$systemTime).";当前的系统时间:".date("Y-m-d H:i:s");app('api_log',['config'=>['type'=>'file']])->tag("uniqid")->write($msg,'warning','file');app('alarm')->alarm($msg);return app("api_result")->returnArray(3,['msg'=>'系统时间错误','time'=>date("Y-m-d H:i:s")]);}}catch(\Exception $e){$msg = "文件:" . $e->getFile() .";代码不可执行,错误行:".$e->getLine().";错误信息:".$e->getMessage();app('api_log',['config'=>['type'=>'file']])->tag("uniqid")->write($msg,'error','file');app('alarm')->alarm($msg);return app('api_result')->echoJson(3,['msg'=>$msg]);}}/*** UUID* 生成uuid*/public function uuid(){//uuid=>time based,uuid3=>name based & MD5,uuid4=>random,uuid5=>name based & SHA1$data = ['uuid1'=> (Uuid::uuid1())->toString(),'uuid3'=> (Uuid::uuid3(Uuid::NAMESPACE_DNS, 'key'))->toString(),'uuid4'=> (Uuid::uuid4())->toString(),'uuid5'=> (Uuid::uuid5(Uuid::NAMESPACE_DNS, 'key'))->toString()];return app('api_result')->echoJson(0,$data);}/*** @param int $number 个数* @param int $length 数字的长度* @param array $data 已有的数字id, 需注意:其中以数组中最后一个数字id作为最小的数字起始值* 批量生成 alpha_id 数字id*/public function alphaId( $number = 1, $length = 8, $data=[]){try{$min = 1;//系统最大正整数$limitNum = PHP_INT_MAX;if($length>=strlen($limitNum)){$max = $limitNum;for($i=0; $i < strlen($limitNum)-1 ; $i++){(int)($min *= 10);}$msg = "数据长度过大,已自动限制;最小值:".$min."(".strlen($min).");最大值:".$max."(".strlen($max).");数字长度参数:".$length.";个数:".$number.";alpha_id:".json_encode($data);app('api_log',['config'=>['type'=>'file']])->tag("alpha_id")->write($msg,'warning','file');}else{$max = 9;for($i=0; $i < $length-1 ; $i++){(int)($min *= 10);(int)($max .= 9);}}//需先计算长度 -- 由于$data一直在变化,所以需先赋值 $total$total = ($number-count($data));//批量生成数据for($i = 0; $i < $total; $i++){if(count($data) == 0){//计算分割段之间的差值$rand = ($max-$min) / ($number) ;$randNum = mt_rand( $rand/2, $rand );//取分割段差值中随机数 + 最小值 = 第一个值$data = [ $min +  $randNum ];}else{//如果数组已存在,则读取最后一个数字id作为最小值// $lastValue = array_pop($data);$lastValue = $data[count($data)-1];//计算分割段之间的差值$rand = ($max - $lastValue) / ($number - count($data)) ;//数据长度小于对应的个数if($rand<1){$msg = "差值:".$rand.";最小值:".$min."(".strlen($min).");最大值:".$max."(".strlen($max).");最后的数字:".$lastValue.";数字长度参数:".$length.";个数:".$number.";alpha_id的个数:".count($data);app('api_log',['config'=>['type'=>'file']])->tag("alpha_id")->write($msg,'warning','file');// app('alarm')->alarm($msg);return app('api_result')->returnArray(3,['msg'=>$msg]);}$randNum = mt_rand( $rand/2, $rand );$value = $lastValue + (($randNum>0) ? $randNum : 1);array_push( $data , (int) $value );}}//去重,继续计算长度,足够则不再进行计算,否则继续追加数据$data = array_unique( $data );//验证数组的长度是否足够长if( count($data) >= $number ){$msg = "成功创建alpha_id:".count($data).";最后一个数字id:".$data[count($data)-1];app('api_log',['config'=>['type'=>'file']])->tag("alpha_id")->write($msg,'info','file');return app('api_result')->returnArray(0,$data);}//出现重复数字id 删除重复id,继续追加数字id return $this->alphaId($number,$length,$data);}catch(\Exception $e){$msg = "文件:" . $e->getFile() .";代码不可执行,错误行:".$e->getLine().";错误信息:".$e->getMessage();app('api_log',['config'=>['type'=>'file']])->tag("uniqid")->write($msg,'error','file');app('alarm')->alarm($msg);return app('api_result')->echoJson(3,['msg'=>$msg]);}}/*** 销毁容器*/public function __destruct(){// app('api_log',['config'=>['type'=>'file']])->tag("uniqid")->write('__destruct','info','file');}}