短网址
顾名思义,短网址就是一个很短的链接而已。在生活中我们经常能收到诸如以下短信。
若我们点开此链接会发现实际的URL十分长。
https://fast-wallet.finzjr.com/file/marketing/20210624/f1739910-5531-4380-b33b-d8dc94d294b1.html?fy=1&from=701&e_channel=duanxin_new_dianshang_0801
似乎这只是做了一个转换呀!有什么高级的呢?长链不行吗?这是怎么做到的?这是我初次遇见短链时抛出的种种问题,相信大家也会由此困惑。接下来将一一解答。
短链的优势
- 短信发送是有字数限制的,若发送长链可能连广告内容都没处写了;
- 短链在内容上对用户友好,不会像长链一样一堆字符;
- 链接太长在有些平台上无法自动识别为超链接,只能用短链
短链的基本原理
通过简单的浏览器抓包我们可以很快速的了解短链跳转的基本原理:
可以看到请求短链后,返回了状态码 301(重定向)与 location 值为长链的响应,然后浏览器会再请求这个长链以得到最终的响应,整个交互流程图如下:
短链的生成
短链是由固定短链域名 + 长链映射成的一串字母组成的,生成的那一串字母就是整个短网址系统的核心所在,如何将长链转换为一小段字符是首要考虑的任务。
Hash?
上述提到短网址系统中做了短链与长链的映射,提起映射大家首先想到的就是Hash了,将长链使用相应的Hash算法生成对应的HashCode,可以以此为短链。
随机再去重?
除了使用Hash算法这一高大上的方式,我们可以随机生成一个6为的短链,首先去数据库中查询是否存在,若存在则重新生成,否则直接使用。
代码实现如下所示:
private String longToShort(String longUrl){
while(true){
String shortUrl = UUID.randomUUID().toString().replaceAll("-","");
UrlEntity urlEntity = urlMapper.selectByShortUrl(shortUrl);
if(urlEntity != null){
// 有重复
continue;
}
// 没重复,直接插入
urlMapper.insertShortUrl(shortUrl,longUrl);
}
}
数据表如下所示:
+----+-----------+----------------------------+
| id | short_url | long_url |
+----+-----------+----------------------------+
| 1 | feafea + http://www.chengpengper.cn |
| 2 | sfevef + http://www.chengpengper.cn |
| 3 | zdfesa + http://www.chengpengper.cn |
| 4 | csedaq + http://www.chengpengper.cn |
| 5 | cesade + http://www.chengpengper.cn |
| 6 | jkonlo + http://www.codelife.cn |
| 7 | podexe + http://www.codelife.cn |
+----+-----------+----------------------------+
62进制
为什么是62进制?那是因为0-9、a-z、A-Z
共62个字符。
我们将一个6位的shortUrl看做一个62进制数,如'feafea'对应的十进制为13951303002。若我们能生成一个唯一的十进制数,自然可以将其转换为62进制数。在单体系统中我们可以借助MySql的自增主键完成唯一ID的生成,再将其转换为62进制的6位短链,而通过短链查询长链只需要将62进制转为十进制(即ID),根据ID查询即可。
进制转换核心代码如下所示:
public class UrlUtils {
static final String str = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
static final int MAX_LEN = 6;
/**
* 进制转换 Base62
*/
/**
* id 转 shortUrl
* @param shortUrl 短网址
* @return 短网址对应的 id
*/
public static long shortUrlToId(String shortUrl){
long id = 0;
char[] chars = shortUrl.toCharArray();
for(int i=0;i < chars.length; i++){
id = id * 62 + toBase62(chars[i]);
}
return id;
}
private static int toBase62(char ch){
return str.indexOf(ch);
}
/**
* id 转 短网址
* @param id id
* @return id 对应的短网址
*/
public static String idToShortUrl(Long id){
StringBuilder shortUrl = new StringBuilder();
while ( id > 0){
shortUrl.append(str.charAt((int)(id % 62)));
id /= 62;
}
while(shortUrl.length() < MAX_LEN){
shortUrl.append("0");
}
return shortUrl.reverse().toString();
}
}
重定向
重定向跳转的核心方法如下所示:
@GetMapping("/{shortUrl}")
public String shortTolongUrl(@PathVariable String shortUrl, HttpServletResponse response){
String longUrl = urlService.shortTolong(shortUrl);
response.setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY);//301
response.setHeader("Location", longUrl);
response.setHeader("Connection", "close");
return "redirect:"+longUrl;
}
数据库变化
和随机生成再去重中的数据表不同,我们不需要再保存具体的短网址字符串了。
+----+----------------------------+
| id | long_url |
+----+----------------------------+
| 1 | http://www.chengpengper.cn |
| 2 | http://www.chengpengper.cn |
| 3 | http://www.chengpengper.cn |
| 4 | http://www.chengpengper.cn |
| 5 | http://www.chengpengper.cn |
| 6 | http://www.codelife.cn |
| 7 | http://www.codelife.cn |
+----+----------------------------+
- 5位 = 62^5 = 9亿
- 6位 = 62^6 = 570亿
- 7位 = 62^7 = 35000亿
所以,6位的62进制足够我们使用了。
自定义短链
有些时候用户不希望系统自动生成短链,而是希望自定义短链。
新建自定义短链表
不建议在原有的表中添加一列表示自定义短链,因为自定义短链的使用相对较少,否则会导致表中大量空缺。所以直接新建一个表吧~
DROP TABLE IF EXISTS `test_custom`;
CREATE TABLE `test_custom` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`custom_url` varchar(6) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '自定义短链',
`long_url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '长链',
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `custom_long_unq`(`custom_url`, `long_url`) USING BTREE COMMENT '建立自定义url和长链的唯一索引,防止重复'
) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
交互图
在短链的跳转部分与之前发生了一些改变,需要先查询custom表,判断是否是自定义短链。
301 or 302?
从计算机网络了解到重定向有301重定向和302重定向,并且短网址系统中也有使用这两种重定向方式的,到底应该选择哪种重定向方式呢?
- 301,代表 永久重定向,也就是说第一次请求拿到长链接后,下次浏览器再去请求短链的话,不会向短网址服务器请求了,而是直接从浏览器的缓存里拿,这样在 server 层面就无法获取到短网址的点击数了,如果这个链接刚好是某个活动的链接,也就无法分析此活动的效果。所以我们一般不采用 301。
- 302,代表 临时重定向,也就是说每次去请求短链都会去请求短网址服务器(除非响应中用 Cache-Control 或 Expired 暗示浏览器缓存),这样就便于 server 统计点击数,所以虽然用 302 会给 server 增加一点压力,但在数据异常重要的今天,这点代码是值得的,所以推荐使用 302!
版权属于:带翅膀的猫
本文链接:https://chengpengper.cn/archives/164/
转载时须注明出处及本声明
学到了学到了,301,302的区别,以及长链转为短链的几个办法。
1:hashcode 会产生hash冲突
2.随机再去重 缺点会随着数据量的增加而变慢(可以随机生成满足条件的数越来越少了)
3.64进制,无重复,且表示范围很大以长度为6的短地址来说,可以表示的范围为64的6此方
4.自定义短链 为了不产生大量null值,新建一个表进行查询操作OωO