前几天项目出了一些问题,祸源就在平台短链应用的是第三方(新浪短链t.cn),所有使用新浪短链的平台都未能逃过本次关服的洗刷。那么这个问题就警醒我们,第三方的服务固然快,也相对比较好,但是这些突如其来的问题也要让我们知道 免费的服务并不能作为长久之计,以前github上也出现过一些项目停止维护或者闭源的情况。 所以系统对于第三方服务的容灾措施一定要做好,以将损失降到最低。

第三方平台的好处

它提供的短链服务不会出现短链地址重复短链重定向失败等问题,所以用稳定的高效算法打造出来的短链平台必然会让平台损失降到最低

file

本篇应用自己服务器搭建短链平台 前期准备:一个短域名、many台能用的服务器,一套短链生成算法 前提: 需要号称国内最好用的工具包:hutool4.x && lombok 1.18.x

<dependency>
 	<groupId>cn.hutool</groupId>
   <artifactId>hutool-core</artifactId>
   <version>4.4.2</version>
</dependency>

<dependency>
	<groupId>org.projectlombok</groupId>
	<artifactId>lombok</artifactId>
	<version>1.18.4</version>
	<scope>provided</scope>
</dependency>

用过短链的人都知道,短链的计算主要是t.cn后面的那串字符串 http://t.cn/Rc37zpz 从左边那个网址可以发现 后面那个字符串大概范围是0---Z,所以,我们可以从这块开始着手,短链为了保证唯一性,需要在一个可控的范围里面生成唯一的值,这个时候我们可以想到时间戳+随机数,时间戳是唯一的,但是时间戳是一串数字,如何能将数字转化成唯一标识的短字符串呢?

答案在下面⤵️

进制的转化

想到了禁止转化,由于我们目标生成串是0---Z的随机值,0---Z之间的length是62位,时间戳是10进制,所以我们可以用10进制转化62进制的做法来进行转化,那么这样做可行吗?

来试试… 这里我直接贴上10进制转62进制的代码,代码跟10进制转16一样,别被62进制吓坏了~~

    /**
     * 初始化 62 进制数据,索引位置代表字符的数值,比如 A代表10,z代表61等
     */
    private static String chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
    private static int scale = 62;

   /**
     * 将数字转为62进制
     * @param num    Long 型数字
     * @param length 转换后的字符串长度,不足则左侧补0
     * @return 62进制字符串
     */
    public static String encode(long num, int length) {
        /**
         * 随机生成7——length位的短链
         */
        int [] arr = NumberUtil.generateRandomNumber(7,length,1);
        StringBuilder sb = new StringBuilder();
        int remainder ;

        while (num > scale - 1) {
            /**
             * 对 scale 进行求余,然后将余数追加至 sb 中,由于是从末位开始追加的,因此最后需要反转(reverse)字符串
             */
            remainder = Long.valueOf(num % scale).intValue();
            sb.append(chars.charAt(remainder));

            num = num / scale;
        }

        sb.append(chars.charAt(Long.valueOf(num).intValue()));
        String value = sb.reverse().toString();
        return org.apache.commons.lang.StringUtils.leftPad(value, arr[0], '0');
    }

这里我们需要一个目标长度,也就是10进制的数字如果不足以转化62进制可以在用0来补,比如 传入1 并且生成一个长度为7的结果如下:

file

不足位用0补也就是这个道理

那么如果说在同一个时间毫秒下有多次请求会不会生成同一个地址,答案是 会!

所以我们应该怎么做呢?

###可以给时间戳加一个随机的3——4位的数字用来控制并发下的短链生成,实验证明,这样做的冲突率很低很低~

理论说完,下来可以看代码啦~

 /**
     * 生成一个length位的数字
     * @param length
     * @return
     */
    public static long genRandomNumber(int length,int size){
        StringBuilder sb = new StringBuilder();
        //这里用random保证完全随机,如果实在生成的一样,我也没办法
        int [] arr = NumberUtil.generateRandomNumber(0,(int)Math.pow(10d,(double)length),size);
        for (int i = 0; i < size; i++) {
            sb.append(Math.abs(arr[i]));
        }
        return Long.valueOf(sb.toString());
    }

贴上调用类:

public static String genShorUrl(){
        try {
            String  timeStrampStr = String.valueOf(System.currentTimeMillis());
            //生成当前时间戳
            String ret = StringUtils.encode(Long.valueOf(timeStrampStr+StringUtils.genRandomNumber(5,1)),10);
           log.info("generat short key is >>>>>>>{}",ret);
            return ret;
        }catch (Exception e){
            e.printStackTrace();
            System.out.println(">>>>>>>>>>"+e);
            return genShorUrl();
        }
    }

然后将两个随机的数字组成一个新的数字,切记这块不能用两个数字相加的形式,因为这块可能随机数和时间戳之和相等,但两个拼接成字符串就不会相等了,所以一定要用字符串的拼接】】

file

工具类也就是上述操作,其余均可调用该工具类

测试:

测试地址

码云地址:码云地址

file