93

基于php实现多进制转换与兑换码生成的探索 - 流水无痕

 6 years ago
source link: https://hanxv.cn/archives/111.html?
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

基于php实现多进制转换与兑换码生成的探索

发表于 2018-01-15

  |   分类于

默认

  |   暂无评论

最近要做一个兑换码生成的功能,之前有做过32位唯一码生成器,但是在业务需求中,32位的兑换码有些过长了,用户在应用内填写的时候会比较麻烦,不是很友好,倒是可以做成二维码的形式扫一下就行了,但是业务中还是存在输入兑换码的行为,所以本篇主要是关于以尽量短的字符来生成兑换码,同时要保证唯一性以及生成机制复用性(也就是利用这套机制可以生成不同种类的兑换码)的探索

以下示例代码均基于TPRCMS编写

探索一: 进制转换

生成的32位唯一码是16进制的哈希字符串,我就在想是不是可以通过提高进制来缩短字符串长度,所以有了如下的代码

代码地址: 多进制转换器ConvertLogic
其中关于10进制与62进制互转的部分,参考了《PHP 10进制与62进制互转,可用于生成短网址》

$uuid = "cd5fd2cfeb40aafe060f4d9597348be7";
$str = ConvertLogic::convert( $uuid, 16, 62);
string(32) "cd5fd2cfeb40aafe060f4d9597348be7"
string(22) "6fxdxREtzxq6qNdSghGm7t"

经过转换后发现,长度最多压缩到21~22位,感觉还是有些长

探索二: uniqid()转62进制

uniqid()可以生成一个13位以上的16进制唯一码,将它转为62进制,会得到一个更短的字符串

$uuid = uniqid('code');
$resule = ConvertLogic::convert( $uuid, 16, 62);

//输出
//string(17) "5a5c5b182386"
//string(12) "7hoyVkRTi "

通过多次生成,从结果观察来看,有以下几个不足:

1.用这种方法生成的一批兑换码,只有后几位是变动的,生成间隔很近的话,只有最后2位不一样,前面的都一样,这样可能会造成结果比较好猜,容易被试出来。
2.唯一性不足。在批量并发多机器生成的时候,很难保证唯一性

探索三: 随机生成12位字符串,用redis保证唯一性

//$category_uniq是类目的hash,下面这句代码主要目的是保证同一类目下不存在相同的兑换码
RedisService::redis()->switchDB()->hSetNx('code_hash_list_'.$category_uniq,  $code, $category_uniq);
//如果同类目下已存在相同的兑换码,会返回false

随机字符生成代码

/**
     * 随机字符串生成器
     * @param $length
     * @param string $strPol
     * @return null|string
     */
    public static function getRandChar($length = 15,$strPol = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz"){
        $str = null;

        $max = strlen($strPol)-1;

        for($i=0;$i<$length;$i++){
            $str.=$strPol[rand(0,$max)];//rand($min,$max)生成介于min和max两个数之间的一个随机整数
        }

        return $str;
}

通过串行100万次随机生成发现,随着数据的逐渐增多,重复的概率会逐渐上升,这就意味重新生成的次数越来越多。

探索四: 利用redis计数器"领票",用事务操作保证计数器的准确,一个兑换码领一个票

4位票号 + 12位随机码
4位票号的目的是为了保证唯一性,随机码是为了防止被轻易试出正确值。
这样即便知道了前面4位是票号,加一就行,但是后面12位可就难试了。
而且4位的票号可以支持一个类目有14538000个兑换码(62进制,"ZZZZ"-"1000"),即便是票号不够了,那票码长度+1即可
这个机制还有一个好处就是,可以根据情况自由修改兑换码长度
比如,现业务只需生成几百个码,而且对安全性没有太多要求,那就可以只需要“2位票码+4位随机符”就可以了

  • private static function ticket($category_uniq , $baseCount){
          $key = 'code_ticket_'.$category_uniq;
          RedisService::redis()->switchDB()->watch($key);
    
          $count = RedisService::redis()->switchDB()->multi()
              ->incr($key)
              ->exec();
          if($count === false){
              return self::ticket($category_uniq, $baseCount);
          }
          $ticket = $baseCount + $count;
    
          return $ticket;
      }

兑换码批量生成完整逻辑代码 : CodeLogic

code snippet CDKEY Producer

利用票号转62进制的思路,其实也可以做短网址
仅需要6位就可以满足500多亿的网址
而每次要新增短网址,只需要到后端领一个票码,并建立票码和真实网址的映射关系(KV)就可以了


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK