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

文章目录

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


前言

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

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

答案是:映射

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

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


一、短链

URL 短链,就是把原来较长的网址,转换成比较短的网址,比如这样:https://s.xxx.com/1WB5A3。那这样设计的好处?

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

其次,省钱省成本:在短信中,如果含长网址的短信内容超过 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 短链跳转原理:

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

https://wx36fb82dbb481a6a2.shop.xxxxxx.com.cn/shop/nw0f7c60fd7000/success/19515?type=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()));
    }

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/2841.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

QMessageBox手动添加按钮并绑定按钮的信号

视频展示效果&#xff08;结合代码看效果更佳哦&#xff0c;代码在最下面&#xff09;&#xff1a; QMessageBox手动添加有重试效果的按钮效果图&#xff1a; 点击详细文本之后展开如下图&#xff1a; 图标可选&#xff1a; QMessageBox::Critical错误图标QMessageBox::NoIco…

第二十一天 数据库开发-MySQL

目录 数据库开发-MySQL 前言 1. MySQL概述 1.1 安装 1.2 数据模型 1.3 SQL介绍 1.4 项目开发流程 2. 数据库设计-DDL 2.1 数据库操作 2.2 图形化工具 2.3 表操作 3. 数据库操作-DML 3.1 增加(insert) 3.2 修改(update) 3.3 删除(delete) 数据库开发-MySQL 前言 …

深度学习:GPT1、GPT2、GPT-3

深度学习&#xff1a;GPT1、GPT2、GPT3的原理与模型代码解读GPT-1IntroductionFramework自监督学习微调ExperimentGPT-2IntroductionApproachConclusionGPT-3GPT-1 Introduction GPT-1&#xff08;Generative Pre-training Transformer-1&#xff09;是由OpenAI于2018年发布的…

从0到1深度学习环境搭建

目录第一步&#xff1a;安装anaconda第二步&#xff1a;创建一个虚拟环境试一下第三步&#xff1a;确定cuda算力&#xff0c;配置cudapytorch官网找版本pycharm配置pycharm进行设置setting 能够打开conda的shell终端如何给下载的项目设置合适的环境如果必须要低版本的pytorch才…

智驾芯片“性价比之王”凭何抢滩增量市场?

未来几年&#xff0c;智能驾驶功能将进入跨越式升级的阶段&#xff0c;同时L2将快速普及&#xff0c;L2进入集中放量的阶段。 包括自动泊车 (APA)、家庭区域记忆泊车 (HAVP)、交通拥堵辅助 (TJA)、高速辅助驾驶 (HWA)、自动辅助导航驾驶 (NOA) 等在内的功能已为普通车主耳熟能…

美颜sdk的动态面具、3D面具实现流程

在美颜sdk的实现中&#xff0c;面具是很重要的一个部分&#xff0c;不管是动态面具还是3D面具都需要实现的&#xff0c;我们在开发中常用的是动态面具和3D面具。但是两种面具有很多不同之处&#xff0c;比如制作材料、制作方式等等。在这里我们先来了解一下动态面具和3D面具是如…

8个不能错过的程序员必备网站,惊艳到我了!!!

程序员是一个需要不断学习的职业&#xff0c;不少朋友每天来逛CSDN、掘金等网站&#xff0c;但一直都抱着“收藏从未停止&#xff0c;学习从未开始”的态度&#xff0c;别骗自己了兄弟。在编程体系中&#xff0c;有很多不错的小工具&#xff0c;可以极大得提升我们的开发效率。…

电容在微分、积分电路中的本质以及应用

很多朋友觉得PID是遥不可及&#xff0c;很神秘&#xff0c;很高大上的一种控制&#xff0c;对其控制原理也很模糊&#xff0c;只知晓概念性的层面&#xff0c;知其然不知其所以然&#xff0c;那么本期从另类视角来探究微分、积分电路的本质&#xff0c;意在帮助理解PID的控制原…

第十四届蓝桥杯三月真题刷题训练——第 21 天

目录 第 1 题&#xff1a;灭鼠先锋 问题描述 运行限制 代码&#xff1a; 思路&#xff1a; 第 2 题&#xff1a;小蓝与钥匙 问题描述 答案提交 运行限制 代码&#xff1a; 思路 : 第 3 题&#xff1a;李白打酒加强版 第 4 题&#xff1a;机房 第 1 题&#xff1…

存储专题扩容,HA、LB分布式存储

一、架构与存储的关系一个新的硬盘在linux系统里使用一般来说就三步:(分区,格式化)-挂载-使用blocklvs:四层负载均衡&#xff0c;nginx、haproxy四层和七层都有redis、memcache缓存中间件是缓存后端数据库读的信息。高端的容器技术&#xff0c;一旦系统出现可以可以直接重装系统…

【springboot】读写分离:

文章目录一、mysql主从复制&#xff08;从库可以有多个&#xff09;&#xff1a;【1】提前准备好两台服务器&#xff0c;分别安装Mysql并启动成功【2】配置---主库Master【3】配置---从库Slave【4】克隆的虚拟机导致mysql主从UUID一致怎么修改&#xff1a;【5】测试二、读写分离…

springboot学生综合测评系统

031-springboot学生综合测评系统演示录像2022开发语言&#xff1a;Java 框架&#xff1a;springboot JDK版本&#xff1a;JDK1.8 服务器&#xff1a;tomcat7 数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09; 数据库工具&#xff1a;Navicat11 开发软件&…

uniapp封装各个时间方法

难点&#xff1a;在项目中我们经常会用到时间转换或时间比对加减问题为了方便很多页面去调用时间方法&#xff0c;我把时间方法封装成了公共方法1.首先在根目录创建文件夹与pages平级&#xff0c;我这里创建了plugins文件夹2.其次在plugins文件夹下面创建index.js文件&#xff…

LeetCode题解 20(17,79) 电话号码的字母组合,单词搜索<回溯>

文章目录电话号码的字母组合(17)代码解答单词搜索(79)代码解答电话号码的字母组合(17) 思路: 根据题意我们必须根据数字获取对应的字符数组&#xff0c;因此我们先定义1个字符数组表示这个电话表 private String[] letters {"","","abc","…

C语言例程:学生成绩管理程序

学生成绩管理程序 实例说明 编制一个统计存储在文件中的学生考试分数的管理程序。设学生成绩以一个学生一条记录的 形式存储在文件中&#xff0c;每个学生记录包含的信息有姓名、学号和各门功课的成绩。要求编制具有以 下几项功能的程序&#xff1a;求出各门课程的总分&#…

Redis单线程还是多线程?IO多路复用原理

目录专栏导读一、Redis版本迭代二、Redis4.0之前为什么一直采用单线程&#xff1f;三、Redis6.0引入多线程四、Redis主线程和IO线程是如何完成请求的&#xff1f;1、服务端和客户端建立socket连接2、IO线程读取并解析请求3、主线程执行请求命令4、IO线程会写回socket和主线程清…

cron表达式 详解

corn表达式是&#xff1a;由若干数字、空格、符号按一定的规则&#xff0c;组成的一组字符串&#xff0c;从而表达时间的信息。 好像和正则表达式有点类似哈&#xff0c;都是一个字符串表示一些信息。Cron 表达式生成器&#xff1a; https://www.smart-tools.cn/cron简介Cron 表…

部署私有npm 库

使用verdacciohttps://verdaccio.org/安装verdaccio使用npm全局安装npm install -g verdaccio安装完成以后&#xff0c;输入verdaccio -h出现如下相关提示&#xff0c;说明verdaccio安装成功。运行verdaccio直接执行verdaccio出现如下相关提示&#xff0c;说明verdaccio启动成功…

【OJ比赛日历】快周末了,不来一场比赛吗? #03.25-03.31 #12场

CompHub 实时聚合多平台的数据类(Kaggle、天池…)和OJ类(Leetcode、牛客…&#xff09;比赛。本账号同时会推送最新的比赛消息&#xff0c;欢迎关注&#xff01;更多比赛信息见 CompHub主页 或 点击文末阅读原文以下信息仅供参考&#xff0c;以比赛官网为准目录2023-03-25&…

React 入门(超详细)

目录前言&#xff1a;一、React 简介1. 什么是 React2. React 的特点3. React 高效的原因4. React 官网5. React的主要原理6. Facebook为什么要建造React?二、React 的基本使用1. 基础代码2. 效果3. 相关 js 库4. 创建虚拟DOM的两种方式5. 虚拟DOM与真实DOM6. 虚拟DOM与真实DO…
最新文章