为什么需要短链?
有时候,平台的地址会异常的长,比如一次搜索的地址:
如果这个地址仅仅在网站内部,不需要用户操作是自动生成和跳转的,那也没有把它缩短的必要。但是如果我想把这个地址分享到外部平台或者发送给别人,那使用这种地址就很痛苦了。由此,我们需要设计一个系统,能够把这么长的地址,转换成一个相对较短的地址。并且当用户点击短链时,还能回到原来长链对应的内容。
此外,使用短链的好处还有:
这种方法的流程是,先根据长链内容,生成一个全局唯一的ID,然后将这个数字ID转换成用62进制表示,得到一个短码。保存短码和长链的对应关系,后续就可以根据短码获得真实的长链
之所以是62进制,是因为26个大写字母+26个小写字母+10位数字。
我们可以在本地维护一个62位元素的数组,例如:
char[] chars ={'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'};
然后将唯一ID,转成62进制,方法如下:
StringBuilder sb = new StringBuilder();
while (id > 0) {
sb.append(chars[(int) (id % chars.length)]);
id = id / chars.length;
}
System.out.println(sb.reverse());
测试一下:
// 假设得到的唯一ID是 987654321 ,那么转换得到的短码是
BE0GAZ
// 假设得到的唯一ID是 987654322 ,那么转换得到的短码是
BE0GAa
1、编码表顺序打乱
如果编码表完全按照字母顺序,那么有心之人只要连续生成两个短链,就知道短链生成的规则了,进而可以获得一些隐秘信息。
实际使用中,可以将编码表顺序打乱,不完全按照字母顺序来,即便是连续生成几个短链,也难以猜到编码表规则
2、保留一位字母
如果编码表完全是26个大写字母+26个小写字母+10位数字,那么根据唯一ID生成短码的时候,就会出现短码长度不一致的情况,比如
// id = 100 短码是Bm
// id = 987654321 短码是BE0GAZ
为了保持所有生成的短码长度一致,可以预留一位字母,当生成的短码长度不够时,填充默认的字母
关于短码的长度,可以预估一下短链的数量。采用61进制时,6位短码就可以支持五百多亿的短链了
3、检查是否重复
如果生成唯一ID的策略不能保证一定全局唯一,最好还是加上一个重复检查
关于重复检查的方法,推荐使用布隆过滤器。布隆过滤器是一种高效的、内存占用非常小的“检查是否存在”的数据结构。redis、guava等组件都有相应的实现
当短链生成后,用户点击短链时,是如何跳转到真实的地址呢?
浏览器会失败HTTP请求返回的不同code,当服务端返回301和302时,浏览器都会跳转到新的地址。区别是
服务端实现重定向的方式,只需要在controller中返回:
HttpHeaders headers = new HttpHeaders();
headers.setLocation(new URI("长链内容"));
return new ResponseEntity<>(headers, HttpStatus.FOUND);