zoukankan      html  css  js  c++  java
  • work_36_如何实现短链接的设计

    1.常常收到某东的短信,并且附带一个链接,点击这个链接会跳转到某个指定的网址,这是怎么实现的呢?

    首先,我们先看看当当的短链接http://3.n/XilLrd6
    它是由两个部分组成
    http://3.n/:短链接系统的域名地址
    XilLrd6:请求参数
    请求http://3.n/XilLrd6地址后,返回状态如下所示

    2.短链跳转的基本原理

    用户访问短链地址然后重定向到原来的地址。

    在HTTP协议中,30X状态代表的是重定向的状态。其中可以是301 也可以是302。

    301 代表永久重定向。对于GET请求, 301跳转会默认被浏览器cache。也就是说,用户第一次访问某个短链接后,如果服务器返回301状态码,则这个用户在后续多次访问同一短链接地址,浏览器会直接请求跳转地址,而不会再去短链接系统上取!

    这么做优点很明显,降低了服务器压力,但是无法统计到短链接地址的点击次数。

    302代表临时重定向。对于GET请求, 302跳转默认不会被浏览器缓存,除非在HTTP响应中通过 Cache-Control 或 Expires 暗示浏览器缓存。因此,用户每次访问同一短链接地址,浏览器都会去短链接系统上取。

    这么做的优点是,能够统计到短地址被点击的次数了。但是服务器的压力变大了。

    下面说最关键的一段,怎么将http://ihelp.jd.com压缩为XilLrd6字符

    3.算法原理

    首先呢,我们需要一张表来存储,长短链接间的映射关系。表结构如下

    列名说明
    id BIGINT,自增主键
    url 长地址,也就是需要跳转的原地址

    好的,假设我们此时表里的数据如下

    idurl
    1 http://ihelp.jd.com/1
    2 http://ihelp.jd.com/2

    我们此时拿自增id作为短链接的key。假设域名http://dwz.win是短链接系统,也就是说请求:
    (1)http://3.n/1会跳转http://ihelp.jd.com/1;
    (2http://3.n/2会跳转http://ihelp.jd.com/2;

    这么做,也不是不行,有两个缺点你要评估能不能接受!

    • (1)如果数据比较大,比如几百亿,你的url地址依然过长
    • (2)你的数据具有规律性,别人用一个简单的脚本就可以遍历出你的跳转地址!

    为了解决上面的两个缺点,我们增加一个列,用来存储key值。此时表结构如下

    列名说明
    id BIGINT,自增主键
    key 短串,需要加唯一索引
    url 长地址,也就是需要跳转的原地址

    我们为了缩短id的长度呢,一般可以这么做。由于我们的短链接是由 a-z、A-Z 和 0-9 共 62 个字符可以选择。因此,我们可以讲十进制的数字id,转换为一个62进制的数,例如201314就可以转换为Qn0。
    算法如下

    public class Demo {
        public static void main(String[] args) {
            String s = toBase62(201314);
            System.out.println(s);
        }
    
    
        private static final String BASE = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
    
        public static String toBase62(long num) {
            StringBuilder sb = new StringBuilder();
            do {
                int i = (int) (num % 62);
                sb.append(BASE.charAt(i));
                num /= 62;
            } while (num > 0);
    
            return sb.reverse().toString();
        }

    另外,我们需要引入一个全局发号器,一直返回全局自增的ID。相当于,我们的短链接系统先去请求这个全局自增ID,然后将全局自增ID转换为62进制的数,作为key。

    接下来,解决第二个问题!数据具有规律性的问题。毕竟你转换为62进制后,只是解决了数据过长的问题,数据规律性问题还是没解决。
    因此,我们需要引入一个随机算法。那么此时,你的考虑点在于,你是否要根据key值,反推出全局id值!来抉择不同的随机算法!
    (1)不希望反推出全局ID
    OK,那就用一个洗牌算法,打乱算出的值。比如十进制的201314就可以转换为Qn0。然后再使用洗牌算法,可以返回n0Q、Q0n....其中之一。但是会有一定几率冲突,多洗几次就行。
    (2)希望反推出全局ID
    OK,那就在得到Qn0这个数字后,将其转换为二进制数。然后在固定位,第五位,第十位...(等等)插入一个随机值即可。
    至于如何反推也很简单,你拿到短链接key后,将固定位的数字去除,再转换为十进制即可。

    讲到这里,就基本将key如何生成的逻辑讲清楚了。那么用户在点击短链接的时候,例如地址http://dwz.win/nXR,短链接系统解析出key为nXR,根据唯一索引去表中将nXR对应的url返回即可。

    4.细节优化

    (1)分库分表
    如果这个系统是放在公网,给大家使用的。建议上来就分库分表,数据量过1000万是很容易的。这里涉及到一个问题,拿全局发号器给的自增id做分片健,还是拿转换后的key做分片键。

    显然,用转换后的key做分片键会更容易一些。如果用ID做为分片键,存在两个问题!
    (1)用户请求的key,需要做一个逆运算推算回ID,然后根据ID,再去对应表里去找,增加响应时间。
    (2)根据选择的随机算法不同,key不一定能够推算回ID值。这种情况下,只能每张表去查,更慢。

    所以用key做分片键,再适合不过了。拿到用户请求的KEY后,直接定位到对应的表里将url取出即可。

    (2)读写分离
    这种系统显然,读远大于写。建议可以考虑做读写分离。

    (3)引入缓存
    假设,我们在一个时间。给手机推送短信链接的短信后。显然,后面的一段时间内,对该短链接的请求量会大大提升。没有必要每次都去数据库查询,因此可以引入redis缓存。

    (4)全局发号器用其他算法行不行
    可以。这里只是要一个全局唯一ID而已。自己要估算好,使用其他算法所带来的性能影响。以及采用其他算法,会不会造成生成的生成的ID过于规律。

    (5)防攻击
    做好被恶意攻击的准备,防止自增ID的值,被全部耗光。

  • 相关阅读:
    fescar中文官网
    mybatis 中的 update 返回值你真的明白吗
    数据库读写分离搭建
    git 回退各种场景操作
    听说noip2015有幻方
    noi2015的回忆和教训
    bzoj4026
    bzoj4127
    bzoj2119
    关于fft的一点总结
  • 原文地址:https://www.cnblogs.com/asndxj/p/14113211.html
Copyright © 2011-2022 走看看