百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

分布式系统进阶二十一之短链接生成原理

myzbx 2025-06-30 18:50 1 浏览

前言

短链接(Short URL)是一种通过缩短网页链接长度来方便分享的技术。相比于传统的长链接,短链接更简洁明了,更易于在社交媒体等平台上分享和传播。在本文中,我们将会详细解释短链接的定义、作用及其构成原理。

短链接的定义

短链接是指将原本的长网页链接通过转化处理成一段更短的字符编码,简化原链接的访问路径,例如将

https://mp.weixin.qq.com/s/PgJyQO8LEhx47IJqFjaV1w 转化为短链接:https://9e6.cn/1。这个短链接通常由几个字符组成,以便在分享时节省字符数和提高可读性。虽然短链接已经存在了一段时间,但它在社交媒体中的广泛使用已经使它成为许多人的熟悉工具。

短链接的作用

1、短链接不仅易于分享,还可以提高可读性。在社交媒体和其他共享平台上,链接通常限制字符数量,短链接能够在丰富文本的同时保持链接的可用性。例如,通过短链接更容易在 Twitter、微博、Instagram 等应用程序中分享链接。

2、通过使用短链接,可以掩盖长URL。它可以帮助防止诈骗攻击等恶意行为。因为这一点,短链接的使用得到了很多的认可。

短链接的构成原理

我们使用百度搜索"hello world",链接为 https://www.baidu.com/s?ie=utf-8&f=8&rsv_bp=0&rsv_idx=1&tn=baidu&wd=hello%20world&rsv_pq=8487bffe00068c60&rsv_t=

a9e0f5b6haiMQwAi4N2y8PHDv37rM6sjjKrHJb6KdMGg2dQuUjAnmSEnXtE&rqlang=cn&rsv_enter=1&rsv_sug3=10&rsv_sug1=9&rsv_sug7=100。然后用百度短链接服务压缩一下上面的长链接,压缩后的链接为:http://dwz.cn/5DDXhH。可以看到,压缩后的链接长度比原链接明显变短了。

操作流程就是对 原URL 进行运算,得到一个较短的唯一字符编码,并将其附加到短链接的域名之后,形成一个短链接。

常见的短链接压缩算法

常见的短链接压缩算法有两种,第一种是对 URL 进行hash运算,在得到的hash值上做进一步运算,得到一个较短的hash值。hash运算简单易实现,但是有一定的冲突率。随着 URL 压缩数量的增加,冲突数也会增加,最终导致一部分用户跳转到错误的地址上,影响用户体验。这里的 hash 算法我们选择比较有代表性的 MurMurHash,是一种简单、高效、散列均匀的算法,众多开源框架中都采用了这种算法,比如 Redis。

package org.example.controller;

import cn.hutool.core.util.HashUtil;

public class ShortLink {
    public static void main(String[] args) {
        String url = "https://www.toutiao.com/c/user/token/MS4wLjABAAAAemUlIefV8HQP3wf9cclqau8z9BkHS72bBLC1bYW3cCY/?source=list&log_from=887a4ccca794f_1719837932675";
        // MurmurHash算法32-bit实现
        int num = HashUtil.murmur32(url.getBytes());
        System.out.println(num);
        System.out.println(to62HEX(num));
    }

    private static String to62HEX(int num) {
        num = Math.abs(num);
        String chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";

        StringBuilder sb = new StringBuilder();
        int remainder;


        while (num > 62 - 1) {
            remainder = num % 62;
            sb.append(chars.charAt(remainder));
            num = num / 62;
        }

        sb.append(chars.charAt(num));

        return sb.reverse().toString();
    }
}

第二种是通过数据库自增ID或分布式key-value系统模拟发号器进行发号压缩URL。两种方式各有优劣。发号器发号压缩 URL 优缺点恰好和hash压缩算法相反,优点是不存在冲突问题。缺点是,实现上稍复杂,要协调发号器取初始号。

使用发号策略压缩URL

发号策略是这样的,当一个新的链接过来时,发号器发一个号与之对应。往后只要有新链接过来,发号器不停发号就好。举个例子,第一个进来的链接发号器发0号,对应的短链接为 xx.xxx/0,第二个进来的链接发号器发1号,对应的短链接为 xx.xxx/1,以此类推。发号器发出的10进制号需要转换成62进制,这样可以大大缩短号码转换成字符串后的长度。比如发号器发出 10,000,000,000 这个号码,如果不转换成62进制,直接拼接在域名后面,得到这样一个链接 xx.xxx/10000000000。将上面的号码转换成62进制,结果为AOYKUa,长度只有6位,拼接得到的链接为 xx.xxx/AOYKUa。可以看得出,进制转换后得到的短链接长度变短了一些。6位62进制数,对应的号码空间为626,约等于568亿。也就是说发号器可以发568亿个号,这个号码空间应该能够满足多数项目的需求了,所以基本上不用担心发号器无号可发的情况。上述是发号策略压缩URL的原理,实际在写代码的过程中还需要考虑很多细节,比如缓存,存储等。

需求设计

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

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

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

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

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

考虑的问题?

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

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

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

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

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

短链接实现

数据表定义

# 短链表
create table `short_link`
(
    `id`             bigint primary key auto_increment comment '主键ID',
    `short_link`     varchar(32)  not null default '' comment '短链接',
    `long_link_hash` bigint       not null default 0  comment  'hash值',
    `long_link`      varchar(128) not null default '' comment '长链接',
    `status`         tinyint      not null default 1  comment '状态:1-可用,0-不可用',
    `expiry_time`    datetime     null comment '过期时间',
    `create_time`    datetime     not null default current_timestamp comment '创建时间',
     PRIMARY KEY (`id`),
  	UNIQUE KEY `uk_short_link` (`short_link`),
  	UNIQUE KEY `uk_md5` (`long_link_hash`)
) comment '短链表';

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

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

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

生成方法

具体生成短链的方法我们前面已经提过了

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

短链跳转原理

首先开发一个简单的 web应用 测试

package org.example.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletResponse;

@RestController
public class ShortLinkController {

    /**
     *
     * @param shortLink : A6fIF
     * @param response
     * @throws Exception
     */
    @GetMapping("/{shortLink}")
    public void link(@PathVariable("shortLink") String shortLink, HttpServletResponse response) throws Exception {
        // query redis for shortLink
        String longLink = "https://www.toutiao.com/c/user/token/MS4wLjABAAAAemUlIefV8HQP3wf9cclqau8z9BkHS72bBLC1bYW3cCY/?source=list&log_from=887a4ccca794f_1719837932675";
        
        response.sendRedirect(longLink);
    }
}

访问地址:http://127.0.0.1/A6fIF,浏览器就会跳转到设定的 longLink 地址。其中A6fIF 是我们生成的短链。

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

  • 1)客户端通过浏览器访问短链
  • 2)服务端返回 301 / 302 重定向码并携带 location= 原链接
  • 3)客户端浏览器重新访问原链接

这里啰嗦一下301和302的跳转在短链接服务使用场景下的区别:用户第一次访问某个短链接后,如果服务器返回301状态码,则这个用户在后续多次访问同一短链接时,浏览器会直接请求跳转地址,而不是短链接地址,这样一来服务器端就无法收到用户的请求。如果服务器返回302状态码,且告知浏览器不缓存短链接请求,那么用户每次访问短链接,都会先去短链接服务端取回长链接地址,然后在跳转。从语义上来说,301跳转更为合适,因为是永久跳转,不会每次都访问服务端,还可以减小服务端压力。但如果使用301跳转,服务端就无法精确搜集用户的访问行为了。相反302跳转会导致服务端压力增大,但服务端此时就可精确搜集用户的访问行为。基于用户的访问行为,可以做一些分析,得出一些有意思的结论。比如可以根据用户IP地址得出用户区域分布情况,根据User-Agent消息头分析出用户使用不同的操作系统以及浏览器比例等等。

相关推荐

使用 Siemens Teamcenter Digital Reality Viewer 打造逼真的数字孪生

现代产品通常由数百万个部件组成,需要复杂的设计和协作。工业世界在管理复杂性方面面临重大挑战,传统的可视化工具无法渲染这些大型的多CAD组件,从而无法充分利用数字孪生的优势。为了解决这些难题,西门子...

如何在JavaScript中实现数字输入框的范围限制?

在JavaScript语言中,实现数字输入框的范围限制可以通过多种方式实现,最常见的方式是利用JavaScript的事件监听机制,和通过JavaScript的条件判断语句来实现范围限制。以...

2.3.8.J速算24点终极挑战(速算24点题目及答案)

各位数学高手、脑力达人,今天给大家带来一道超烧脑的**24点挑战**!**数字:2、3、8、J(J=11规则很简单:**用加减乘除和括号,把四个数字组合成24!每个数字只能用一次!****看起...

分布式系统进阶二十一之短链接生成原理

前言短链接(ShortURL)是一种通过缩短网页链接长度来方便分享的技术。相比于传统的长链接,短链接更简洁明了,更易于在社交媒体等平台上分享和传播。在本文中,我们将会详细解释短链接的定义、作用及其构...

一、SpringBoo中Mybatis多数据源动态切换

我们以一个实例来详细说明一下如何在SpringBoot中动态切换MyBatis的数据源。一、需求1、用户可以界面新增数据源相关信息,提交后,保存到数据库2、保存后的数据源需要动态生效,并且可以由用户动...

「JS 逆向百例」层层嵌套!某加速商城 RSA 加密

声明本文章中所有内容仅供学习交流,敏感网址、数据接口均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关,若有侵权,请联系我立即删除!逆向目标目标:某加速商城登录接口主页:a...

Spring Data Jpa 介绍和详细入门案例搭建

1.SpringDataJPA的概念在介绍SpringDataJPA的时候,我们首先认识下Hibernate。Hibernate是数据访问解决技术的绝对霸主,使用O/R映射(Object-Re...

SpringBoot 开发 Web 系统,快速入门指南!

01、背景介绍在之前的文章中,我们简单的介绍了SpringBoot项目的创建过程,了解了SpringBoot开箱即用的特性,本篇文章接着上篇的内容继续介绍SpringBoot用于we...

Nacos3.0重磅来袭!全面拥抱AI,单机及集群模式安装详细教程!

之前和大家分享过JDK17的多版本管理及详细安装过程,然后在项目升级完jdk17后又发现之前的注册和配置中心nacos又用不了,原因是之前的nacos1.3版本的,版本太老了,已经无法适配当前新的JD...

golang语言的魅力精华之玩转通道channel 值得你反复阅读100遍

通道用例大全在阅读本文之前,请先阅读通道一文。那篇文章详细地解释了通道类型和通道值,以及各种通道操作的规则细节。一个Go新手程序员可能需要反复多次阅读那篇文章和当前这篇文章来精通Go通道编程。本文...

2小时快速搭建一个高可用的IM系统

“笔者2019年参加了一次Gopher大会,有幸听探探的架构师分享了他们2019年微服务化的过程。图片来自Pexels本文快速搭建的IM系统也是使用Go语言来快速实现的,这里先和...

分库分表以后,如何管理几万张分片表?

大家好,我是小富~ShardingSphere实现分库分表,如何管理分布在不同数据库实例中的成千上万张分片表?上边的问题是之前有个小伙伴看了我的分库分表的文章,私下咨询我的,看到他的提问我第一感觉就是...

Spring Boot JDBC 与 JdbcTemplate 全面指南(万字保姆级教程)

一、SpringBootJDBC基础1.1JDBC简介与演进JDBC(JavaDatabaseConnectivity)是Java语言中用来规范客户端程序如何访问数据库的应用程序...

Flink SQL 知其所以然(六)| 维表 join 的性能优化之路(上)

废话不多说,咱们先直接上本文的目录和结论,小伙伴可以先看结论快速了解博主期望本文能给小伙伴们带来什么帮助:背景及应用场景介绍:博主期望你能了解到,flinksql提供了轻松访问外部存储的loo...

大数据Hadoop之——Flink Table API 和 SQL(单机Kafka)

一、TableAPI和FlinkSQL是什么TableAPI和SQL集成在同一套API中。这套API的核心概念是Table,用作查询的输入和输出,这套API都是批处理和...