短链接是怎么设计的?带你入门

编程入门 行业动态 更新时间:2024-10-16 22:24:37

短链接<a href=https://www.elefans.com/category/jswz/34/1770344.html style=是怎么设计的?带你入门"/>

短链接是怎么设计的?带你入门

文章目录

  • 前言
  • 一、短链
    • 1、原理
      • 1.1 短链生成原理
      • 1.2 短链跳转原理:
    • 2、设计:
      • 2.1 短链需求
      • 2.2 考虑的问题?
  • 二、实践案例
    • 1、设计表:
    • 2、生成短链:


前言

说到 URL 你肯定不陌生,浏览器输入一段 URL,立马就跳转到你想要的网站,不过你应该也遇到过一些带了很多参数、特别长的 URL,看起来就乱糟糟的,能不能把它变短一点?

首先你要知道的是,长链是没法压缩成短链的,那我们这里怎么设计短链?

答案是:映射

将需要的长链生成对应的短链,你可以把这个映射关系放在缓存、也可以放在数据库。然后,每次访问短链的时候,都需查到这个对应关系,并重定向到真正长链接对应的网址。

短链设计本身很简单,不过现在的需求量非常大,这个量级上去了,简单的东西都会变得复杂,因此,本文从简入深探讨锻炼设计原理与方法。


一、短链

URL 短链,就是把原来较长的网址,转换成比较短的网址,比如这样:。那这样设计的好处?

首先,网址短、美观、便于发布、传播,可以写更多有意义的文字。

其次,省钱省成本:在短信中,如果含长网址的短信内容超过 70 字,就会被拆成两条发送,而用短链则可能一条短信就搞定,如果短信量大也可以省下不少钱。

再则,易识别:我们平常看到的二维码,本质上也是一串 URL ,如果是长链,对应的二维码会密密麻麻,扫码的时候机器很难识别,而短链则不存在这个问题。

另外,出于安全考虑,不想让有意图的人看到原始网址。

1、原理

1.1 短链生成原理

获取短链:

短链标志(flag)长度一般 5 - 7位,一般由数字、字母(可选择区分大小写)等字符组成。有几种常见的获取方式:

1)截取前缀:我们可以从众多 hash 算法中选择一种(比如 MD5),将 url 经过 hash 计算之后得到一串字符,然后截取前缀 5 - 7位即可。

案例如下:

    public static String toHex(String url) {String hash = Md5.hash(url);return hash.substring(0, 6);}

2)除法取模:选择一种 hash 算法可以输出 32 位、64位 … 的整数 x,对字符集合大小取模,得到下标 id,记录下来,从指定的字符编码集合中找到对应字符,循环该操作直到 x 小于编码集大小。

这里的 hash 算法我们选择比较有代表性的 MurMurHash,是一种简单、高效、散列均匀的算法,众多开源框架中都采用了这种算法,比如 Redis。

这种算法不用你自己去实现,很多工具包已经帮你实现了,我们这里采用 Hutool 工具包里的实现,如下:

int hash = HashUtil.murmur32(url.getBytes());

然后将整数散列值通过 除法取模 运算并从指定字符编码集选定字符输出,如下:

    private static String to62HEX(int num) {num = Math.abs(num);String chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";StringBuilder sb = new StringBuilder();int remainder;while (num > 62 - 1) {remainder = Long.valueOf(num % 62).intValue();sb.append(chars.charAt(remainder));num = num / 62;}sb.append(chars.charAt(Long.valueOf(num).intValue()));return sb.reverse().toString();}

解决短链碰撞:

所谓碰撞就是出现了重复,短链重复了肯定不行,因为映射关系需要唯一对应。

你也知道,很多常见的散列算法输出的散列值,都有可能出现极小概率冲突,尤其是我们这里只有 5 - 7 位,冲突概率就进一步加大,怎么解决?考虑短链的应用场景,我们介绍常见的一种方式:

加随机串,多次计算,直到找到没有冲突的短链:

    // 已被占用,则增加随机延长串进行重新计算for (int i = 0; i < 10; i++) {flag = genShortLink(link + RandomStringUtils.randomAlphabetic(10));if (shortLinkDao.findByFlag(flag).isEmpty()) {break;}}

以上是根据我们目前的场景设计,循环 10 次基本够用,如果你的场景不够用,你可以考虑酌情优化。

1.2 短链跳转原理:

当你从浏览器输入短链地址并回车时,浏览器地址栏立马出现类似于这样一长串的地址:

=source&sign=01FB60A657676C6905C84F8C90A1DA99&timestamp=1678366800

这个地址才是短链真正的目标地址。也就是说,短链不是真正的目标网址,我们需要重定向找到短链映射的目标地址,怎么找?从哪里找?

还是那句话,完全取决于你的场景选择,如果你的用量非常大,设计一个单独的短链服务并独立部署也是很有必要的。

当然,如果你的用量一般,直接在基础服务上设计短链接口也是没有问题的。

然后,重定向?请求一般是先通过网关,所以,在网关,需要识别到当前是 短链 请求,然后直接调用接口查询短链的映射地址,并给客户端返回重定向信息。

具体我这里给一个例子:

访问流程基本就以下几点:

  • 1)客户端通过浏览器访问短链

  • 2)服务端返回 301 / 302 重定向码并携带 location= 原链接

  • 3)客户端浏览器重新访问原链接

2、设计:

前文我们讲到了短链的基本原理,实际场景变化万千,我们来看看通常情况下,你在设计短链的时候需要考虑到哪些问题。

2.1 短链需求

1)唯一:给定原始的长 URL ,短链服务能生成比它短且唯一的 URL,即 短链

2)映射:用户点击短链 , 能跳转到原始的长 URL

3)过期:短链经过一定时间后,会过期

4)REST API:接口需要设计成 REST API

5)过滤:有些关键字眼,比如 NSSBM 比如这种,可酌情考虑过滤掉。

2.2 考虑的问题?

1)过期:短链一般都不是长期有效的,3个月、6个月、一年 … 处理方式很多,你可以定期手动删除、任务定时检查删除 …

2)过滤:有些客户可能对特殊的字眼比较敏感,比如 SB、SC、MD … 可以酌情考虑过滤掉。

3)安全:短链不可被预测,否则简单的遍历就能把所有短链都遍历完,空耗系统资源。因此,常见的递增主键的方式一般不能采用。

4)高性能:生成短链的过程,以及从短链跳转到原始 URL 要近实时,你可以考虑预先生成足够多的短链、映射关系缓存等等 …

5)高可用:服务不能存在单点故障,如果你的请求量比较大或者要求低容错率,一般你需要考虑集群部署,分散单节点的压力。

6)系统容量预估:需要提前预估你的需求场景,尤其是量大的时候,如何快速存、快速取是一个设计要点。

7)系统扩展:这个一般也是量大的时候需要考虑。

二、实践案例

这里我们看看如何快速设计一个可线上使用的短链服务。

1、设计表:

CREATE TABLE `t_short_link` (`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',`flag` varchar(10) NOT NULL COMMENT '短链标识',`link` varchar(2048) NOT NULL COMMENT '原始链接',`md5` varchar(64) NOT NULL COMMENT '原始链接的md5',`created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',`deleted` tinyint(4) DEFAULT '0',PRIMARY KEY (`id`),UNIQUE KEY `uk_flag` (`flag`),UNIQUE KEY `uk_md5` (`md5`)
) 

其中,flag 就是我们的短链标志,通过 flag 可以找出具体的映射的 link(url),然后重定向访问 link 即可。

另外,这里我们记录了原始链接的 MD5 值,有什么作用?你想,如果你想要判断某个长链接是否已经存在,要去对比原链接吗?太麻烦,直接用 MD5 值来判断,非常方便。

最后,还是建议你从数据库层面确保短链的唯一性,因此,我们这里将 flag、md5 字段设置成了唯一键,确保不会出现重复。

2、生成短链:

业务层面还是比较简单了,主要有查询判断、去重、以及散列冲突解决。

    public String createShortLink(Params params) {String link=params.getLink();String host=params.getHost();String md5 = DigestUtils.md5DigestAsHex(link.getBytes(StandardCharsets.UTF_8));Optional<ShortLink> shortLinkOptional = shortLinkDao.findByMd5(md5);// 已存在,直接返回if (shortLinkOptional.isPresent()) {return getShortLink(shortLink, host);}// 加锁避免相同链接并发处理final RLock lock = redissonClient.getLock(CacheConstants.LOCK_SHORT_LOCK_CREATE + md5);try {if (lock.tryLock(3, TimeUnit.SECONDS)) {// 二次检测shortLinkOptional = shortLinkDao.findByMd5(md5);if (shortLinkOptional.isPresent()) {return getShortLink(shortLinkOptional.get(), host);}String flag = "";flag = genShortLink(link);if (shortLinkDao.findByFlag(flag).isPresent()) {// 已被占用,则增加随机延长串进行重新计算for (int i = 0; i < 10; i++) {flag = genShortLink(link + RandomStringUtils.randomAlphabetic(10));if (shortLinkDao.findByFlag(flag).isEmpty()) {break;}}}ShortLink shortLink = new ShortLink(flag, link, md5);shortLinkDao.save(shortLink);return getShortLink(shortLink, host);}} catch (Exception e) {if (e instanceof InterruptedException) {Thread.currentThread().interrupt();}log.error("[short_link] 短链生成失败", e);throw new BizException(ErrorEnum.SHORT_LINK_FAIL);} finally {if (lock.isHeldByCurrentThread() && lock.isLocked()) {lock.unlock();}}return link;}

生成短链,核心就是要生成一个不重复的 短链标志(flag),一般短链的 flag 会设计成 5、6 位由数字、字母组成的随机字符串,原则上长度越长,冲突的概率越小。

具体生成短链的方法我们前面已经提过了,我们简单回顾下:

    public static String genShortLink(String link) {return to62HEX(HashUtil.murmur32(link.getBytes()));}

至此,一个基本可用短链能够满足一般的场景了 …

更多推荐

短链接是怎么设计的?带你入门

本文发布于:2024-03-07 01:55:45,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1716542.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:是怎么   带你   入门   链接

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!