Java-如何设计短链
===========
前言
—
短链,通俗地讲就是将原本非常长的URL链接精简,让短链接也能访问到原本的长链接。比如掘金给我们发送的系统通知。长链和短链让我们都能访问到同一篇文章。
长链:juejin.cn/post/738499…
那既然有了长链,为什么还需要短链?
- 方便记忆,用户体验更好。短链简短,更加方便记忆,用户看到也更加舒适,想象一下要是掘金给你发个通知,文字没几个,一段下来全是url地址,那用户看到是非常糟糕的。
- 某些功能需要。某些功能发送的字数是有限制的,如果URL太长,那基本功能将无法满足,比如给用户发送短信时,供应商是有字数限制的;评论也是,URL占据过多,真正的内容表达就少了。
- 方便后续统计追踪。当用户把文章分享出去,我们需要统计不同用户分享出去的点击量时,便可以在短链信息中带上用户ID。
核心讲解
原理
原理还是简单的,当用户访问短链时,我们给他返回302的状态,在header中带上Location,告诉浏览器这是一个临时文件,真正的文件需要访问Location中的地址。
请求流程
功能实现
长链->短链
那么长链又是转成短链的呢?后端接收到前端传过来的长链后,首先用hash算法将长链转成10进制的一组数,用md5或者sha都是可以的,只不过md5或者sha都是非对称加密,效率没有用hash高。这里我用的是hutoool工具的fnvHash,可以转成32位的10进制;之后再将10进制转成62进制进一步缩短字符长度。
// 计算出长链的hash
int fnvHash = HashUtil.fnvHash(longChain);
// 10进制转62进制短链字符串 "1eDpPm"
String shortChain = DecimalUtil.decimalTo62Base(fnvHash);
记录短链map
将长链对应短链map关系保存,可以保存到Redis,也可以保存到Mysql等数据库中。
CREATE TABLE `chain` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键id',
`long_chain` varchar(255) DEFAULT NULL COMMENT '长链',
`short_chain` varchar(255) DEFAULT NULL COMMENT '短链',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=18 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='长链->短链表';
短链重定向
当前端发送访问短链请求时,先去找到短链对应长链,将重定向请求返回浏览器,浏览器再去请求长链。
/**
* 短链重定向
*/
@GetMapping("/a/{shortChain}")
public void query(@PathVariable String shortChain, HttpServletResponse response) {
// 数据库查询对应长链
Chain chain = chainMapper.selectOne(new LambdaQueryWrapper<Chain>().eq(Chain::getShortChain, shortChain));
String longChain = chain.getLongChain();
//编码,防止url有中文
String encodeLongChain = URLUtil.encode(longChain);
// 拼接域名、端口、访问文件前缀
String url = "http://localhost:19090/file/common"+encodeLongChain;
//重定向
//response.sendRedirect(encodeUrl);
response.setStatus(302);
response.setHeader("location",url);
}
特别注意
- 在生成短链之前,我们需要检查一下这个长链是否已经转换过短链了,如果已经转换过了,就将Redis或者数据库中记录的短链返回。同时,如果是特别热点的数据,就像上面说的,掘金发给全部用户活动的短链,我们可以保存到Redis中,快速返回结果。
- 🤔❓在长链用hash计算短链时,如果产生哈希冲突怎么办?哈希冲突产生就会出现不同长链对应相同的短链,虽然这个概率非常低。
解决方法:
- 将数据库中short_chain设置成不可重复。
- 使用布隆过滤器,将短链都放到布隆过滤器中,如果布隆中已经存在对应短链,就在长链中添加个随机字符串再次生成短链,如果还存在就再加个随机字符串,再次生成。
// 创建布隆过滤器
BitMapBloomFilter bitMap = BloomFilterUtil.createBitMap(10);
String shortChain;
while(true){
// 计算出长链的hash
int fnvHash = HashUtil.fnvHash(longChain);
// 10进制转62进制短链字符串 "1eDpPm"
shortChain = DecimalUtil.decimalTo62Base(fnvHash);
if(bitMap.contains(shortChain)){
longChain += RandomUtil.randomString(1);
} else {
bitMap.add(shortChain);
break;
}
}
- 🤔❓不同用户对于同一个长链怎么生成短链?
当需要统计同一篇文章不同人分享时的点击量,这时长链和短链的对应关系就是一对多了,在生成短链的时候就需要把用户的ID加到长链上了。
最终流程
演示
完整代码
前端(vue3):gitee.com/HT3902LY/wr…
后端(Java):gitee.com/HT3902LY/wr…
原文链接: https://juejin.cn/post/7388456632273223731
文章收集整理于网络,请勿商用,仅供个人学习使用,如有侵权,请联系作者删除,如若转载,请注明出处:http://www.cxyroad.com/17776.html