「这是我参与11月更文挑战的第6天,活动详情查看:2021最后一次更文挑战」。
作者:Mintimate
Mintimate’s Blog,只为与你分享
短链接
什么是短链接
短链接,又称缩略网址服务、缩址、短址、短网址、缩略网址、网址缩短、缩短网址、URL缩短等,指的是一种互联网上的技术与服务。
此服务可以提供短URL以代替原来可能较长的URL,将长的URL地址缩短。
用户访问缩短后的URL时,通常将会重定向到原来的URL。
为什么用短链接
使用短链接,主要的场景有:
- Twitter、微博等平台,消息字数限制,使用短链接对原有链接缩短。
- 隐藏Get、PATH参数。
- ……
实例演示
有些小伙伴可能还是没有概念,这里举个腾讯云自带的短链接。比如:腾讯云活动的链接是:
1 | js复制代码https://cloud.tencent.com/act/cps/redirect?redirect=1077&cps_key=&from=console |
而腾讯云对外给的短链接:
1 | js复制代码https://curl.qcloud.com/XnaFxKqr |
可以看到,链接有效地缩短了。同时,已经看不到PATH
和Get
参数。用户访问短链接,会自动301/302跳转到原链接:
实现思路
其实实现的思路很简单,我们生成一个短链接,大概的思路是传入原链接,在后台进行处理后,得到一个唯一识别码,一同存入数据库,最后再把这个唯一识别码回显给用户。
得到短链接后,用户发给其他用户进行访问时,后台根据这个识别码,再进行数据库查询(完善的系统内,还会有Redis进行缓存),最后重定向到原链接即可:
所以,其实实现很简单,要点:
- 生成唯一
识别码
,对应链接,且识别码要短。 - 后台301/302
重定向跳转
。
如何实现上述两个要点呢?
本文以Java(Springboot)为例,其他编程语言可以按图索骥。
唯一识别码
每次后台接收前台的响应,则生成一个识别码存储到数据库,已备后续调取重定向。
这个识别码最好与时间戳有关,同时,如果有多个服务器同时组网,这个识别码最好还要加上机械识别码。
相信很多人已经知道我要用什么了……
综上,我们可以使用雪花ID
;同时,雪花ID为一个Long类型,转换为int类型有19位,肯定是太长了,但是肯定不会重复。
雪花ID
雪花算法(Snowflake)是一种生成分布式全局唯一ID的算法,生成的ID称为Snowflake IDs或snowflakes。
这种算法由Twitter创建,并用于推文的ID。Discord和Instagram等其他公司采用了修改后的版本。
一个雪花ID:
- 前41位是时间戳:
这意味着,雪花ID是可以排序的。
- 之后10位代表计算机ID:便于分布式存储。
- 其余12位代表每台机器上生成ID的序列号:便于分布式存储和集群。
Java版本参考代码:
1 | java复制代码/** |
当然,如果你用使用Mybatis Plus,可以引用Mybatis Plus的IdWorker.getId
方法,生成雪花ID。
生成后的Long类型,我们使用十进制展开,应该是一个17-19位的数字。
六十二进制
因为雪花ID通过十进制展开是一个17-19位的数字,如果直接用来当作短链接,太长了点,我们需要对其缩短。
为了保证唯一,且可对照。我们转换为六十二进制。
原因很简单:六十二进制使用A-Z、a-z和0-9组成。
把十进制,转换为六十二进制,能有效减短长度。
根据Wiki-Base62规定,六十二进制中0-61对应0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
。所以,我们编写编码和解码:
1 | java复制代码/** |
再测试一下:
1 | java复制代码//Test |
输出:
1 | js复制代码1425664925648310274 |
可以看到,这样生成一个短、不可重复的字符串了。这样用于短链接生成,挺合适的:
- 每个短链接程度基本固定。
- 短链接长度不至于过长。
- 生成的短链接可排序(时间排序)
响应头
重定向链接,响应头很重要。Nginx内可以使用配置直接跳转301/302,比如强制HTTPS:
1 | nginx复制代码if ($server_port !~ 443){ |
而我们搭建短链接平台,也利用301或者302进行重定向:
301/302
301和302都是重定向,那它们的区别是什么呢?
- 301:
永久重定向
,在请求的URL已被移除时使用,响应的location首部中应包含资源现在所处的URL - 302:
临时重定向
,和永久重定向类似,客户端应用location给出URL临时定位资源,将来的请求仍为原来的URL。
实际场景里,301在跳转后,浏览器会记住这个跳转,后续请求,不再请求原地址,而是直接请求新地址;所以301一般用于网站域名的迁移,强制网站https等,而302一般是网站维护,需要临时跳转到非维护页面等情况。
那我们搭建短链接平台,需要什么重定向呢?我认为是都可以。使用301重定向,可以减少服务器负载,而使用302重定向,可以方便我们统计链接实际调取次数。
Java内,进行301/302的跳转,其实很简单,使用类RedirectView
,其中的HttpStatus
即可:
1 | java复制代码# RedirectView类 |
实际上,看HttpStatus
的源码,可以看到这里枚举了很多HTTP的响应头:
Maven部署(代码实现)
最后,我们看看实际部署和代码实现。只是随便提供思路,代码可能有逻辑不严谨地方嗷。实际开发,应该还需要Redis缓冲,避免数据库过载。
本次使用MariaDB作为数据库,使用Mybatis Plus对数据库进行操作,Springboot提供框架并方便打包。
再次强调:实际开发,应该还需要Redis缓冲,避免数据库过载。
依赖包
首先,我们创建一个工程,其中Lombok是为了方便实体类生成Set/Get方法:
1 | xml复制代码<dependencies> |
实体类
我们看看短链接实体类:
1 | java复制代码@Data |
其中:
- baseUrl:用户提供的原链接域名,如:
tool.mintimate.cn
。 - suffixUrl:用户提供链接的参数,如:
/user?login=yes
。 - fullUrl:用户提供的原链接,如:
https://tool.mintimate.cn/user?login=yes
。 - shortCode:生成的短链接。
- totalClickCount:统计点击次数(
Hander
自动设置默认值) - expirationDate:失效时间(
Hander
自动设置默认值)
短链接处理
首先,做一个控制器,用来接收用户请求:
1 | java复制代码// 接收原链接,返回短链接 |
之后,看看业务层,我们需要对域名进行加工,先得到一个雪花ID,再对其转至六十二进制,并回显:
1 | java复制代码@Resource |
这个时候,我们使用Postman来测试一下:
可以看到,测试成功。
短链接重定向
短链接重定向,就很简单了。我们写一个请求即可:
1 | java复制代码@ResponseBody |
其中,shortUrlService
的 findURL
就是简单的JDBC查询,不具体实现。
Demo
我根据上述思路,初略搭建一个Demo,并部署在个人服务器:
- 前端:基于Vue,使用element ui和Bootstrap
- 后端:Springboot
我们可以在Linux/macOS上使用curl
测试一下,比如直接用腾讯云轻量应用服务器的Linux远程终端:
1 | arduino复制代码curl -I "https://curl.mintimate.ml/1Hjsg8wDe8i" |
完善思路
可以看到,文章实现的有些粗糙,提供以下完善思路:
- 限制单IP一段时间的请求频率:目前我是使用前端Vue进行控制,但是最好后端也进行控制。
- 数据库优化:目前使用的是MariaDB,如果要更好的体验,或者响应数据量大,使用Redis进行缓冲会更好。
- Cron定时任务:使用雪花ID转六十二进制,在链接长度上,还是有点长,但是安全性应该是很高的;如果降低安全性,并进一步缩短长度,可以创建Cron定时线程,无效旧的短链接。
本文转载自: 掘金