短链是什么原理?怎么实现呢?

news2024/11/27 2:32:01

目录

  • 一、为什么需要短链
  • 二、短链跳转访问原理
  • 三、短链生成实现方案
    • 1、自增序列算法
    • 2、Hash算法
  • 四、代码示例
    • 1、表结构及索引
    • 2、外部依赖
    • 3、Base62Utils
    • 4、DAO层
    • 5、业务层
  • 五、测试用例

一、为什么需要短链

内容营销中给用户推送营销消息最常见的方式就是发短信,比如三大运营商移动、联通、电信平时会发送一些诸如套餐办理、消费查询、话费充值这些短信,还有像银行、云服务厂商等等推送的各种包含查询服务的短信等等。

我们都知道单条短信发送的内容长度是有限制的,而如果要推送包含URL的消息,如果URL太长,不仅影响用户观感,而且会占用太多无用字数。

这时,我们需要将长的URL转换为短URL,也就是接下来我们要说的短链(短域名 + 短请求路径)。



二、短链跳转访问原理

原理还是很简单的,其实就是在后台保存有短链和长链的映射关系,然后进行重定向,让浏览器跳转到对应的长链接。
在这里插入图片描述
举个栗子,原始长链接为:https://www.baidu.com,通过某平台我生成了一个短链接:https://suo.nz/378IQe
在这里插入图片描述
我们可以看到当访问https://suo.nz/378IQe时,后端返回了302,同时多了一个Location响应头,值就是原始链接https://www.baidu.com.

这里有个小问题,关于重定向使用301还是302

编码含义备注
301Moved Permanently永久重定向,表示原 URL 不再被使用,而应该优先选用新的 URL,搜索引擎会直接更新与该资源相关的URL,一般用于网站重构。
302Found临时重定向,搜索引擎不会记录该资源对应的临时链接,一般用于由于不可预见的原因导致该页面暂不可用。
  • 301其实是比较符合HTTP协议语义的,但浏览器会缓存目标网址,下次访问时会直接跳过短链,跳转到目标网址,无法做一些统计,比如短链访问次数等。
    在这里插入图片描述

  • 302:浏览器访问时,会先后访问短链代理服务和目标服务,对服务器的压力也就相应大些,但可以做一些统计。

备注:很多短链生成平台其实都是走的302重定向。



三、短链生成实现方案

1、自增序列算法

常用的自增序列算法有雪花算法、Redis自增、MySQL主键自增等,生成唯一ID后,再转换为62进制字符串,转换后的62进制字符串可用作短链。

问题:为什么需要转换为62进制字符串呢?
因为自增ID会越来越长,经过62进制转换后可以变得更短。

现在说下关于自增序列生成短链的优缺点:

  • 优点:ID唯一,生成的短链不会重复和冲突。
  • 缺点:随着ID越来越大,短链长度也会随之变化,长度不固定。

再说下各种自增序列算法的优缺点:

算法优点缺点
雪花算法高性能,不依赖任何中间件存在系统时钟回拨问题,原始雪花算法长度为64位,生成的ID比较长。
Redis自增高性能,高并发既然是中间件,有维护成本,同时要考虑持久化、灾难恢复等。
MySQL主键自增使用简单,易于扩展高并发下有性能瓶颈 。
  • 301其实是比较符合HTTP协议语义的,但浏览器会缓存目标网址,下次访问时会直接跳过短链,跳转到目标网址,无法做一些统计,比如短链访问次数等。
  • 302:浏览器访问时,会先后访问短链代理服务和目标服务,对服务器的压力也就相应大些,但可以做一些统计。

2、Hash算法

简单来说就是对目标长链接进行hash,然后再对hash值进行62进制编码转换为短链接。Hash算法我们熟知的有MD5SHA等算法。

这两种算法为加密型hash算法,性能相对比较低,这里我们一般采用Google Guava中实现的Murmurhash算法,该算法为非加密型hash算法,相比MD5优点如下:

  1. 速度比MD5快。
  2. 哈希冲突的概率低,该算法支持32位和128位哈希值,MD5也是128位哈希值,基本不用担心哈希冲突。
  3. 离散度高,散列值比较均匀。

关于Murmurhash示例如下:

String url = "https://www.baidu.com/";

// 输出:e9ac4fbdc398e8c104d1b8415f42cbf8
System.out.println(Hashing.murmur3_128().hashString(url, StandardCharsets.UTF_8));
// 输出:06105412
System.out.println(Hashing.murmur3_32_fixed().hashString(url, StandardCharsets.UTF_8));
// 输出:bf447182
System.out.println(Hashing.murmur3_32_fixed().hashLong(Long.MAX_VALUE));

// 转成Long型

// 输出:307499014
System.out.println(Hashing.murmur3_32_fixed().hashString(url, StandardCharsets.UTF_8).padToLong());
// 输出:2188461247
System.out.println(Hashing.murmur3_32_fixed().hashLong(Long.MAX_VALUE).padToLong());

这里说下通过Hash算法生成短链的优缺点:

  • 优点:去中心化,哈希后生成的短链长度基本固定。
  • 缺点:有概率会发生哈希冲突,解决哈希冲突的方法主要有拉链法重新哈希。由于短链是通过哈希值转62进制字符串生成,如果发生哈希冲突,得重新哈希生成。如果存库的话,每次生成短链至少会有一次查询和一次保存操作,有性能损耗。


四、代码示例

接下来的示例我们主要用Hash算法 + Base62 编码生成短链,流程图如下:
在这里插入图片描述

1、表结构及索引

# 短链信息表
create table `t_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 '创建时间'
) comment '短链信息表';
create index idx_sl_hash_long_link on t_short_link (long_link_hash, long_link);
create index idx_sl_short_link on t_short_link (short_link);

2、外部依赖

<!--Google Guava-->
<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>31.1-jre</version>
</dependency>

3、Base62Utils

public abstract class Base62Utils {

	private static final int SCALE = 62;

	private static final char[] BASE_62_ARRAY = {
		'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
		'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
		'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
		'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
		'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'
	};

	private static final String BASE_62_CHARACTERS = String.valueOf(BASE_62_ARRAY);

	/**
	 * 将long类型编码成Base62字符串
	 * @param num
	 * @return
	 */
	public static String encodeToBase62String(long num) {
		StringBuilder sb = new StringBuilder();
		while (num > 0) {
			sb.insert(0, BASE_62_ARRAY[(int) (num % SCALE)]);
			num /= SCALE;
		}
		return sb.toString();
	}

	/**
	 * 将Base62字符串解码成long类型
	 * @param base62Str
	 * @return
	 */
	public static long decodeToLong(String base62Str) {
		long num = 0, coefficient = 1;
		String reversedBase62Str = new StringBuilder(base62Str).reverse().toString();
		for (char base62Character : reversedBase62Str.toCharArray()) {
			num += BASE_62_CHARACTERS.indexOf(base62Character) * coefficient;
			coefficient *= SCALE;
		}
		return num;
	}
}

备注:BASE_62_ARRAY中的字符顺序可以随便打乱,不一定要按顺序排列,打乱安全性更高。

问题:能不能用Base64编码生成短链呢?

JDK8中Base64.getEncoder()获取到的编码器对应的编码字符会包含'+''/'等URL不允许包含的特殊字符,但Base64.getUrlEncoder()获取到的编码器对应的编码字符会把'+''/'分别替换成'-''_',所以其实也是可以的。如下:
在这里插入图片描述

4、DAO层

@Repository
public class ShortLinkManagerImpl implements ShortLinkManager {

	@Autowired
	private ShortLinkMapper shortLinkMapper;

	@Override
	public void saveShortLink(String shortLink, long longLinkHash, String longLink) {
		ShortLinkDO shortLinkDO = ShortLinkDO.builder()
			.shortLink(shortLink)
			.longLinkHash(longLinkHash)
			.longLink(longLink)
			.status(true)
			.build();
		shortLinkMapper.insert(shortLinkDO);
	}

	@Override
	public String getShortLink(long longLinkHash, String longLink) {
		Wrapper<ShortLinkDO> wrapper = Wrappers.lambdaQuery(ShortLinkDO.class)
			.select(ShortLinkDO::getShortLink)
			.eq(ShortLinkDO::getLongLinkHash, longLinkHash)
			.eq(ShortLinkDO::getLongLink, longLink)
			.last(CommonConst.LIMIT_SQL);
		ShortLinkDO shortLinkDO = shortLinkMapper.selectOne(wrapper);
		return Optional.ofNullable(shortLinkDO).map(ShortLinkDO::getShortLink).orElse(null);
	}

	@Override
	public boolean isShortLinkRepeated(String shortLink) {
		Wrapper<ShortLinkDO> wrapper = Wrappers.lambdaQuery(ShortLinkDO.class).eq(ShortLinkDO::getShortLink, shortLink);
		return shortLinkMapper.selectCount(wrapper) > 0;
	}
}

5、业务层

@Service
public class ShortLinkServiceImpl implements ShortLinkService {

	@Autowired
	private ShortLinkManager shortLinkManager;

	@Override
	public String generateShortLink(String longLink) {
		long longLinkHash = Hashing.murmur3_32_fixed().hashString(longLink, StandardCharsets.UTF_8).padToLong();
		// 通过长链接Hash值和长链接检索
		String shortLink = shortLinkManager.getShortLink(longLinkHash, longLink);
		if (StringUtils.isNotBlank(shortLink)) {
			return shortLink;
		}
		// 如果Hash冲突则加随机盐重新Hash
		return regenerateOnHashConflict(longLink, longLinkHash);
	}

	private String regenerateOnHashConflict(String longLink, long longLinkHash) {
		// 自增序列作随机盐
		long uniqueIdHash = Hashing.murmur3_32_fixed().hashLong(SnowFlakeUtils.nextId()).padToLong();
		// 相减主要是为了让哈希值更小
		String shortLink = Base62Utils.encodeToBase62String(Math.abs(longLinkHash - uniqueIdHash));
		if (!shortLinkManager.isShortLinkRepeated(shortLink)) {
			shortLinkManager.saveShortLink(shortLink, longLinkHash, longLink);
			return shortLink;
		}
		return regenerateOnHashConflict(longLink, longLinkHash);
	}

}


五、测试用例

@SpringBootTest(classes = Application.class)
public class ApplicationTest {

	@Autowired
	private ShortLinkService shortLinkService;

	@Test
	public void generateShortLinkTest() {
		String shortLink = shortLinkService.generateShortLink("https://www.baidu.com/");
		System.err.println("生成的短链为:" + shortLink);
	}
}

控制台输出:

生成的短链为:D4PTSU

备注:生成的短链长度基本为6位字符串,还有记得短链代理服务选一个短域名

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Hadoop的MapReduce基本流程体验

目录 MapReduce 编程规范 Mapper阶段 Reducer阶段 Driver阶段 常用数据序列化类型 案例实施 WordCountMapper类 WordCountReducer类 WordCountDriverr 驱动类 HDFS测试 MapReduce 编程规范 用户编写的程序分成三个部分&#xff1a;Mapper、Reducer 和 Driver。 Mappe…

使用粒子效果动画组成模型[自定义shader实现]

文章目录优点实现思路传递给Shader的数据根据模型数据生成数据传递给Shader自定义shader 连接cpu与gpu顶点着色器 计算位置片元着色器优点 性能卓越 上一篇使用的更改坐标实现 9万个点 页面非常卡顿 光是计算9万个点坐标更替的js就已经造成了堵塞 尝试了在顶点着色器中实现动画…

大数据Kudu(五):Kudu基于Cloudera Manager安装及配置

文章目录 Kudu基于Cloudera Manager安装及配置 一、启动CM集群 二、登录ClouderaManager平台安装Kudu

TCO-PEG-RGD 反式环辛烯聚乙二醇线肽RGD

反式环辛烯(TCO)作为亲双烯体与S-四嗪(Tetrazine)在生理条件下的反应有无需催化剂、反应速率快的优点&#xff0c;被广泛应用于生物和材料科学的研究中。 产品名称 TCO-PEG-RGD 反式环辛烯聚乙二醇线肽RGD 中文名称 线肽-聚乙二醇-反式环辛烯 英文名称 TCO-PEG-RGD 分…

“引进来,走出去”,锦江国际集团多重创新力引领绿色新发展

2022年12月13日&#xff0c;由南方财经全媒体集团指导&#xff0c;21世纪经济报道主办的“21世纪住宿业高峰论坛&#xff08;2022&#xff09;暨2022&#xff08;第十九届&#xff09;【金枕头】酒店大赏发布典礼”在上海如期举行。锦江国际集团副总裁周维应邀出席并发表“创新…

【开源项目】SFUD--通用串口Flash驱动库的移植和使用

1.简介 SFUD 是一款开源的串行 SPI Flash 通用驱动库。由于现有市面的串行 Flash 种类居多&#xff0c;各个 Flash 的规格及命令存在差异&#xff0c; SFUD 就是为了解决这些 Flash 的差异现状而设计&#xff0c;让我们的产品能够支持不同品牌及规格的 Flash&#xff0c;提高了…

JeecgBoot部署(Nginx+Tomcat)

环境搭建JeecgBoot搭建、Linux安装Nginx、Linux安装JDK8、Linux安装MySql8、Linux安装Redis、Linux安装Tomcat9前端打包 1.进入目录&#xff1a;cd D:\win11\git_data\jeecg_3.4.4\jeecgboot-vue32.安装依赖&#xff1a;pnpm install-->node_modules3.打包编译&#xff1a;p…

矩阵树定理

用途 矩阵树一般用于生成树计数的问题&#xff0c;比如求一个无向图中生成树的个数。用矩阵树定理能极大地降低时间复杂度。 前置知识&#xff1a;行列式 此部分可粗略浏览&#xff0c;了解即可。 对于一个一阶行列式&#xff0c;可写作 det(a1,1)a1,1det \left( \begin{ma…

[附源码]Node.js计算机毕业设计房车营地在线管理系统Express

项目运行 环境配置&#xff1a; Node.js最新版 Vscode Mysql5.7 HBuilderXNavicat11Vue。 项目技术&#xff1a; Express框架 Node.js Vue 等等组成&#xff0c;B/S模式 Vscode管理前后端分离等等。 环境需要 1.运行环境&#xff1a;最好是Nodejs最新版&#xff0c;我…

大数据学习:shell脚本

文章目录一、执行shell脚本1、直接执行脚本&#xff08;1&#xff09;绝对路径方式执行脚本&#xff08;2&#xff09;相对路径方式执行脚本2、利用source命令执行脚本3、利用bash或sh命令执行脚本二、shell脚本实战任务一&#xff1a;显示当前用户主目录1&#xff09;编写脚本…

3ds Max发生闪退怎么办?

3ds Max闪退 最近&#xff0c;小编在后台收到了一位炫云小伙伴的反馈&#xff1a;“我打开3ds Max后&#xff0c;总是出现闪退&#xff0c;究竟是什么情况&#xff1f;”实际上&#xff0c;闪退也细分为多种类型。例如&#xff1a;打开3ds Max直接闪退&#xff08;logo加载界面…

某轻工制造企业“三步走”战略,搭建一站式数据应用平台

某轻工制造企业成立于1994年&#xff0c;是中国轻工业塑料行业十强企业之一。该企业信息系统之间烟囱化&#xff0c;数据融合难、共享难&#xff0c;无法形成数据资产体系为企业创造价值。因此&#xff0c;该企业与亿信华辰合作&#xff0c;建设一站式数据应用平台&#xff0c;…

【雕爷学编程】Arduino动手做(110)---JDY-31 蓝牙模块

37款传感器与执行器的提法&#xff0c;在网络上广泛流传&#xff0c;其实Arduino能够兼容的传感器模块肯定是不止这37种的。鉴于本人手头积累了一些传感器和执行器模块&#xff0c;依照实践出真知&#xff08;一定要动手做&#xff09;的理念&#xff0c;以学习和交流为目的&am…

【Kubernetes】资源管理(命令)总结

kubernetes&#xff0c;是一个全新的基于容器技术的分布式架构领先方案&#xff0c;是谷歌严格保密十几年的秘密武器----Borg系统的一个开源版本&#xff0c;于2014年9月发布第一个版本&#xff0c;2015年7月发布第一个正式版本。 kubernetes的本质是一组服务器集群&#xff0…

Java+Swing图书管理系统2.0

JavaSwing图书管理系统2.0一、系统介绍二、功能展示1.用户登陆页面2.首页3.图书查询4.图书入库&#xff08;管理员&#xff09;5.图书借还情况&#xff08;管理员&#xff09;6.图书证管理&#xff08;管理员&#xff09;7.借书&#xff08;学生、老师&#xff09;8.还书&#…

pikahcu靶场-12 目录遍历,敏感信息泄露,不安全的URL跳转

目录遍历&#xff0c;敏感信息泄露&#xff0c;不安全的URL跳转 目录遍历漏洞 概述 在web功能设计中,很多时候我们会要将需要访问的文件定义成变量&#xff0c;从而让前端的功能便的更加灵活。 当用户发起一个前端的请求时&#xff0c;便会将请求的这个文件的值(比如文件名称…

Netflix:用神经网络改善视频质量

点击上方“LiveVideoStack”关注我们▲扫描图中二维码或点击阅读原文▲了解音视频技术大会更多信息编者按Editors note眼看用户视频的增长将超过服务器的算力上限&#xff0c;既没有额外的服务器来支持&#xff0c;也不能对用户体验造成大的冲击&#xff0c;Instagram的工程师找…

C罗轮播图(HTML+CSS+JS)

✅作者简介&#xff1a;热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏&#xff1a;前端案例分…

RFM 模型

RFM 模型顾客价值分析顾客价值分析 由于激烈的市场竞争&#xff0c;各个公司相继推出了多样灵活的优惠方式来吸引更多的客户。 对一个没有购买力的顾客&#xff0c;你打电话推销优惠活动毫无作用&#xff0c;可一个高价值顾客&#xff0c;会说有优惠活动怎么不通知我呢&#…

交叉梯度函数的MATLAB实现及代码分享02

交叉梯度函数的MATLAB实现及代码分享02 交叉梯度函数可用于反演成像中。作为一个连接不同物性参数的桥梁&#xff0c;交叉梯度函数可以实现不同物性参数的联合反演成像。 本文是对上一个博文的补充&#xff0c;详见交叉梯度函数的MATLAB实现及代码分享01&#xff0c;上一篇博…