PHP生成唯一RequestID类,使用 session_create_id() 与 uniqid()方法,保证唯一性,提供完整代码及演示,方便大家学习使用。

现在的系统设计一般使用分布式系统,一个请求可能要调用几个微服务处理,最后再把结果返回。

当请求出现问题时,我们很难去跟踪是哪个微服务出现问题。

每个请求访问服务器时,我们可以给这个访问加入一个唯一标识(RequestID),在请求开始,请求过程中,及请求结束时,把这个请求流程关键的数据写入日志(例如访问时的参数,经过那些方法,微服务,结束时返回的数据等),当访问出现问题时用于参考,方便追踪到问题。 

例如一个请求需要经过几个微服务再返回输出

请求 —> A —> B —> C —> A —> 输出

如果访问过程没有输出,或输出错误,我们可以根据RequestID找到A,B,C对应的日志,检查是哪个服务出现问题。 

代码如下:

vim RequestID.class.php

<?php
/**
 * PHP生成唯一RequestID类
 * Date:    2018-04-10
 * Author:  fdipzone
 * Version: 1.0
 *
 * Description:
 * PHP实现生成唯一RequestID类,使用session_create_id()与uniqid()方法实现,保证唯一性。
 *
 * Func:
 * public  generate 生成唯一请求id
 * private format   格式化请求id
 */
class RequestID{ // class start

    /**
     * 生成唯一请求id
     * @return String
     */
    public static function generate(){

        // 使用session_create_id()方法创建前缀
        $prefix = session_create_id(date('YmdHis'));

        // 使用uniqid()方法创建唯一id
        $request_id = strtoupper(md5(uniqid($prefix, true)));

        // 格式化请求id
        return self::format($request_id);

    }

    /**
     * 格式化请求id
     * @param  String $request_id 请求id
     * @param  Array  $format     格式
     * @return String
     */
    private static function format($request_id, $format='8,4,4,4,12'){

        $tmp = array();
        $offset = 0;

        $cut = explode(',', $format);

        // 根据设定格式化
        if($cut){
            foreach($cut as $v){
                $tmp[] = substr($request_id, $offset, $v);
                $offset += $v;
            }
        }

        // 加入剩余部分
        if($offset<strlen($request_id)){
            $tmp[] = substr($request_id, $offset);
        }

        return implode('-', $tmp);

    }

} // class end
?>

 

PHP 文件引用 RequestID 类

<?php
require 'RequestID.class.php';

// 生成10个请求id
for($i=0; $i<10; $i++){
    echo RequestID::generate().PHP_EOL;
}
?>

 

运行输出:

16532925-4502-CDAD-23A2-463FC7B5803A
500B77AD-CD24-0DDA-9E6E-2FDF2DD7CA94
813143D0-958F-9F56-E04F-679598594452
E5EE1B0B-E0D6-3E60-D831-462C5A262FCE
79E714B5-A37F-4B5E-4EDE-83E18391EBF9
E1C440AB-FC2C-AC74-E79A-016FD59D9651
AE483861-1040-BE8D-E523-D7638D0F0D35
BBD7A03A-36C9-24B7-C453-FB1DDD6E201E
BF62C3E6-9C5F-22CB-668D-381863B35268
E97E1F44-F048-962A-5BF7-1113727551B1

  
注意:

session_create_id 方法需要php7.1以上的版本才可使用。

关于session_create_id方法可参考官网说明: https://www.php.net/manual/zh/function.session-create-id.php

session_create_id ([ string $prefix ] ) : string

session_create_id() is used to create new session id for the current session. It returns collision free session id.

If session is not active, collision check is omitted.

Session ID is created according to php.ini settings.

It is important to use the same user ID of your web server for GC task script. Otherwise, you may have permission problems especially with files save handler.

 

 

Web集群全局唯一 request id生成算法, 替代uuid等通用方案

如何为每一个web请求分配一个在全集群范围内都唯一的request id ,却又不想去实现一个复杂的集中式id序列生成器呢? 

UUID? 这或许是个办法,但不觉得不太甘心么? (推荐技术方案之一

下面的这个方式可能可以帮到你:(另一种实现方案,供参考

package test;
import java.util.concurrent.atomic.AtomicLong;
import test.LocalIpAddressUtil;

public class UniqRequestIdGen {

   private static AtomicLong   lastId  = new AtomicLong();                                             // 自增id,用于requestId的生成过程
    private static final long   startTimeStamp = System.currentTimeMillis();                               
    // 启动加载时的时间戳,用于requestId的生成过程
    private static final String ip             = LocalIpAddressUtil.resolveLocalAddress().getHostAddress(); // 本机ip地址,用于requestId的生成过程

    public static void main(String[] args) {
        System.out.println(resolveReqId());
    }

    private static String resolveReqId() {
        // 规则: hexIp(ip)base36(timestamp)-seq
        return hexIp(ip) + Long.toString(startTimeStamp, Character.MAX_RADIX) + "-" + lastId.incrementAndGet();
    }

    // 将ip转换为定长8个字符的16进制表示形式:255.255.255.255 -> FFFFFFFF
    private static String hexIp(String ip) {
        StringBuilder sb = new StringBuilder();
        for (String seg : ip.split("\\.")) {
            String h = Integer.toHexString(Integer.parseInt(seg));
            if (h.length() == 1) sb.append("0");
            sb.append(h);
        }
        return sb.toString();
    }
}

代码说明:

其思路,在注释里已经解释清楚了

这个id包含了本机ip、本应用启动时的时间戳、本应用内部自增id这三个要素,并且以合适的转码方式组合而成,可以简单地做到全局唯一性

生成的唯一性requestId形如:0a11d448iaxk1z35-112 利用它不仅能唯一标识一个请求,还能通过它反查到具体的机器ip

 

 

参考推荐:

UUID 通用唯一识别码及其广泛应用场景

PHP 资源收集整理大全

11个小而美的Python库

Python 和 PHP 对腾讯云签名 hmac_sha256 算法实现

CentOS 配置swap交换区的方法 (Linux 很多地方用到了 UUID)

Linux Shell 生成随机数和随机字符串

shell 脚本的常用功能代码

Kafka 分布式消息系统架构

Java8 十大新特性详解

PHP 优秀资源框架汇总

WordPress 数据库及各表结构