神奇而又有趣的短链服务系统

大家在刷微博或者在接收短信时应该都会注意过类似这样的链接url:

weibo.com/4yBWU
weibo.com/42Ipf
weibo.com/2BmFL
weibo.com/23rwx

这些链接通常非常简短,仅此被称为短链,但是当你点击这些短链后却会发现真实的url其实是一个很长的链接,类似这样:

weibo.com/prd/direction.html?pageId=51e3d088bbfa489e818462a9d84167ff&pwId=dedb75e7516e46b29f181754ecffadb6&channelId=10010001005

效果就是明明你在浏览器中输入的是一个短链接,但是回车后这个链接变成了一个很长的链接。你有没有好奇过为什么微博或者短信中的链接都非常短,这样有什么好处,这一切又是如何实现的呢?

为什么要使用短链?

我们知道发微博是有字数限制的,当博主试图转发一个链接时如果链接本身很长就像上面这个链接的话那么博主的内容会受到很大的限制,因此为了节省链接本身占用的空间微博中大量使用了短链。

而对于短信来说,我们通常收到的垃圾短信其实是有商家希望推广某种商品发出来的,发送短信当然要收商家的钱,这些都是按字数收费的,当然字数越少费用就越低,因此短链也被大量应用于推广短信当中。

那么这一切都是如何实现的呢?

如何实现短链?

短链服务的功能非常简单,无非就是把类似:

weibo.com/prd/direction.html?pageId=51e3d088bbfa489e818462a9d84167ff&pwId=dedb75e7516e46b29f181754ecffadb6&channelId=10010001005

转为类似:

weibo.com/4yBWU

其实也就是把一个“长字符串转为短字符串”,当用户点击短链的时候我们只需要查找该短链对应的真实链接并把用户重定向过去就可以了。

那么这里的问题是该如何把一个长字符串转为唯一的一个短字符串呢?

可能大家第一个想到的就是hash,没错,通过将长字符串进行hash可以实现上述目的,但是不要忘了hash会存在冲突的可能性,也就是有可能两个长链都映射到了某个短链上,有的同学也许会想这样的概率很低吧,但是不要忘了像微博体量的内容,其短链的数量可能会有上百亿,在这种情况下使用hash显然就不满足要求了。

在这里我们需要一个短链和长链是唯一对应的,那么最简单的方法就是给每个长链唯一编号,我们可以从1开始给每个长链依次编号,这样短链的形式就是这了:

weibo.com/1
weibo.com/2
weibo.com/3
weibo.com/4

但是这样的方案会存在很多问题,比如这样简单的方法就给了爬虫很大的便利,只需要简单写个脚本就可以拿到微博几乎所有的内容;而且该方案对于编号很大的长链接来说依然会占用较多的字符,像十亿(1000000000)这样的数字依然占用了10位数,因此简单的编号不可用。

我们看到了简单采用计数的方法有很多缺点,那么该如何改进呢?

本质上这其实就是需要对:

1,2,3,4,5,6,7,8,9,10......

这样的依次递增计数方法换一种不是那么直观而且又节省长度的计数方式。


62进制

说到节省数字本身的长度,有的同学第一反应可能就是使用更大的进制来计数:

对于二进制数来说,6个二进制数字可以表示2^6,也就是64个数;

对于十进制数来说,6个十进制数字可以表示10^6,也就是一百万个数;

对于十六进制来说,6个十六进制数字可以表示16^6,也就是超过一千六百万个数;

而对于人类熟悉的数字(0~9)、小写字母(a~z)、大写字母(A-Z),这些字符加起来有62个数字,因此我们可以使用62进制数,那么6个62进制数字可以表示62^6个数,也就是超过500亿,我们只需要6位62进制数就可以表示超过500亿个短链。

那么我们该如何把一个10进制的id转为一个由数字(0~9)、小写字母(a~z)、大写字母(A-Z)表示的62进制数呢?相信这应该难不倒聪明的你吧,这应该是学习C语言时入门级别的练习题难度,C++版如下所示:

string long_url_2_short_url(long int id){
    string base = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
    string url;
    while(id) {
        url = (char)(base[id % 62]) + url;
        id /= 62;
    }
    return url;
}

代码非常简单,核心仅有6行代码,输入60000000该函数会返回43kjw,显然weibo.com/43kjw 比weibo.com/60000000好多了。

现在我们已经成功的将十进制id转为62进制的短链了,那么给定一个短链我们该如何获取其id呢?代码也非常简单,无非就是把62进制数转为10进制:

long int short_url_2_id(string url) {
    long int id = 0;
    for(int i=0; i<url.length(); i++){
        if ('0'<=url[i] && url[i]<='9')
            id = id*62 + url[i] - '0';
        else if ('A'<=url[i] && url[i]<='Z')
            id = id*62 + url[i] - 'A' + 10;
        else
            id = id*62 + url[i] - 'a' + 36;
    }
    return id;
}

实现短链服务

现在我们已经成功的将长链转为了短链,也可以根据短链获取到长链的id,那么接下来的任务就是实现重定向。

当用户点通过浏览器点击短链时,浏览器会向我们的短链服务发送请求,短链服务接收到情况后根据短链计算出长链的id,查询数据库后得到长链比如叫做real_url,然后告诉用户浏览器“hey,你跳转到的real_url吧”,那么短链服务该如何告诉用户浏览器跳转到真实的链接地址呢?

很简单,我们只需要将返回给浏览器的长链接加一个302的http返回码就可以了,用户浏览器接收到请求后发现是一个302的返回数据,这时浏览器就去请求该真实长链接了。

而我们之所以将其称为短链服务,是因为有一堆服务器,这些服务只做一件事那就是提供短链服务,包括:

  • 将长链转为短链并放入数据库

  • 将用户请求的短链重定向到真实的长链

也就是说对于短链,浏览器至少要发送两次请求才能获得真实的内容,一次发送给短链服务获取真实的长链,再一次请求真实的长链。

总结

在本篇中我们看到了一种很有意思的互联网服务,也就是短链服务,这种服务的目的就在于将一个长链接转为一个很简短的短链,当用户请求短链时将用户浏览器重定向到长链,这里涉及的原理非常简单,就是我们熟悉的进制转换。

但是在这里不想给大家一种互联网服务很easy的感觉,对于真实的短链服务来说还有很多要考虑的因素:

  • 这里的短链实现方式其实依然没有解决很容易被爬虫爬取的缺点,因为62进制的43kj6的下一个数很直观就是43kj7,有没有更好的办法?

  • 由于我们将所有长链都转为了短链,那么当用户请求到来时首先第一步就是请求短链服务,短链服务就成了一个关键点,一旦短链服务不可用影响极大,因此高并发、高可用是短链服务必须具备的,你知道这个短链服务的性能瓶颈会出现在哪里吗,你该如何优化系统性能,又该如何保证高可用呢?

这些都留给大家来思考。

PS:微信公众号从去年开始限制了留言功能,如果你有任何问题欢迎直接在公众号留言。


为你推荐

1,三次握手与Socket API
2,四次挥手与Socket API
3,一个耗时4小时的内存泄漏问题

操作系统系列

基础篇
1,什么是程序

2,程序?进程?傻傻分不清

3,程序员应如何理解内存:上篇
4,程序员应如何理解内存:中篇
5,程序员应如何理解内存:下篇
6,程序员应如何理解CPU:上篇
7,程序员应如何理解CPU:下篇
系统调用篇
8,操作系统是如何看待进程的
9,系统调用是如何实现的
10,程序员应如何理解系统调用:上篇
11,程序员应如何理解系统调用:下篇
12,以发展的眼光来看待操作系统
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值