短链接设计和思考

目录

什么是长链接、什么是短链接?

有了长链接为啥需要短链接呢?

短链接如何跳转到长链接?

长链接如何生成短连接?

1 哈希算法生成短连接

2 ID生成器生成短链接

10进制和62进制转换代码


什么是长链接、什么是短链接?

https://github.com/jack1liu/Java/ 这个地址一共33个字符,属于长链接。当然这个只是示例,真实场景会带有各种参数。

http://d.sb.com/U7eRz 这个地址是上面的长链接地址经过处理得到的,一共21个字符,属于短链接。

有了长链接为啥需要短链接呢?

以发送营销短信为例子,每一条短信字符是有上限的。

  • 如果使用长链接很容易超过单条短信上限,将变成两条短信,成本增加,用户体验也很差。
  • 如果使用短链接,将大大减少字符数目,由于链接字符的变少,文字部分可以变多。

当然了,短链接除了经常使用在营销短信场景,还被广泛应用于生成二维码

短链接如何跳转到长链接?

假设用户收到营销短信,之后点击营销短信里面的短链接,是如何跳转到长链接的呢?

主要为三步:

一 客户端访问短链接地址,短链接服务器校验是否是自己颁发的地址。

二 短链接服务器识别出是自己颁发的地址,302重定向到长链接地址。

三 客户端获取到长链接地址进行访问。

长链接如何生成短连接?

1 哈希算法生成短连接

哈希算法可以将一个不管多长的字符串,转化成一个长度固定的哈希值。

其中比较著名并且应用广泛的一个哈希算法,那就是 MurmurHash 算法。该算法计算速度快和冲突概率小。

GitHub - jack1liu/Java: Java技术栈学习记录 通过 32bits MurmurHash 算法得到哈希值 445113871。

将哈希值拼接域名很容易得到,短网址:http://d.sb.com/445113871

由于 Google 的 guava 包有 MurmurHash 算法的实现。这块附上 guava 的依赖。

<dependency>
	<groupId>com.google.guava</groupId>
	<artifactId>guava</artifactId>
	<version>19.0</version>
</dependency>

能不能让短网址更短呢?

可以的,445113871 是十进制表示,我们可以选择更高进制表示。业内用的最多的是62进制。

为什么要用62进制?64进制不行么?

62进制只含数字+小写字母+大写字母;64进制会含有"/,+"这样的符号,不符合正常URL的字符。而且如果是6位62进制的话,能有560亿种组合,满足业务需求。

我们都知道在使用哈希算法的时候有可能会出现冲突。所谓的冲突就是不同的长链接地址哈希会生成相同的短链接。用户通过短链接不知道访问哪个长链接。

如何解决哈希冲突问题呢?

用 MySQL 保存短链接和长链接的映射关系,短链接字段加上唯一索引。

一 当有一个新长链接需要生成短链接的时候,通过 MurmurHash 算法,62进制转换,生成短链接。

二 在映射表中通过短链接查询,如果没有找到短链接,将短链接和长链接映射关系写入表中。

三 如果映射表有记录,将映射表中得到的长链接和新长链接进行对比,如果相同。直接使用短链接。

四 如果映射表得到的长链接和新长链接不同,出现了哈希冲突。这个时候我们可以给新长链接拼接特定字符再进行哈希。

五 这个特定字符可以自定义,比如[short_url],需要注意的是数据库写入的加上特定字符的,给客户端返回的时候需要去掉特定字符。

2 ID生成器生成短链接

我们维护一个 ID 自增生成器。它可以生成 1、2、3…这样自增的整数 ID。

分布式 id_新猿一马的博客-CSDN博客目录一 为什么需要分布式 ID二 如何生成分布式 ID2.1 数据库自增 ID2.2 UUID 生成2.3 本地时间2.4snowflake 算法2.5 snowflake 算法优化三 参考文档一 为什么需要分布式 ID 一个唯一 ID 在一个分布式系统中是非常重要的一个业务属性,其中包括一些如订单 ID,消息 ID ,会话 ID,他们都有一些...https://blog.csdn.net/jack1liu/article/details/95887201一 当短链接服务接收到一个长链接转成短链接请求之后,它先从 ID 生成器中取一个 id。

二 将 id 转化成 62 进制,将得到的结果拼接到短链接域名后面,得到最终的短链接。

三 将生成的短链接和对应的长链接存储到数据库中。

这块有一个问题,假设相同的长链接两次来生成短链接,这块会生成两个不同的短链接。

从用户的角度来看,是不影响用户使用的,因为不管是哪个短链接,最终都是一个长链接。

从存储的角度来看,如果请求N次,这块的数据存储压力是很大的,而且很多都是脏数据。需要添加唯一索引。

10进制和62进制转换代码

package com.sb.shorturl.util;
import org.apache.commons.lang3.StringUtils;
public class ConversionUtils {
    /**
     * 初始化 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) {
        StringBuilder sb = new StringBuilder();
        int remainder = 0;

        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 StringUtils.leftPad(value, length, '0');
    }

    /**
     * 62进制字符串转为数字
     *
     * @param str 编码后的62进制字符串
     * @return 解码后的 10 进制字符串
     */
    public static long decode(String str) {
        /**
         * 将 0 开头的字符串进行替换
         */
        str = str.replace("^0*", "");
        long num = 0;
        int index = 0;
        for (int i = 0; i < str.length(); i++) {
            /**
             * 查找字符的索引位置
             */
            index = chars.indexOf(str.charAt(i));
            /**
             * 索引位置代表字符的数值
             */
            num += (long) (index * (Math.pow(scale, str.length() - i - 1)));
        }

        return num;
    }
}

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值