IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> PHP知识库 -> php 生成雪花id数组自定义生成id数组生成uuid -> 正文阅读

[PHP知识库]php 生成雪花id数组自定义生成id数组生成uuid

作者:commentBox

秒杀订单需要提前生成订单号时,可以使用雪花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);

            //叠加雪花ID
            for($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'):[];
           
            //已经注册了机器码=返回机器id
            if(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');
    }

}

  PHP知识库 最新文章
Laravel 下实现 Google 2fa 验证
UUCTF WP
DASCTF10月 web
XAMPP任意命令执行提升权限漏洞(CVE-2020-
[GYCTF2020]Easyphp
iwebsec靶场 代码执行关卡通关笔记
多个线程同步执行,多个线程依次执行,多个
php 没事记录下常用方法 (TP5.1)
php之jwt
2021-09-18
上一篇文章      下一篇文章      查看所有文章
加:2021-07-28 07:27:14  更:2021-07-28 07:27:26 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/4 4:38:33-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码