简述
在生活中,我们会通过url访问各种网站,但是一般url太长的话,我们发布到微博,发送短信等都是有长度限制的,这时候可能一条url就超出范围了。所以我们会使用tinyUrl来协助我们解决这类问题。
短链接好处
- 发短信,发布评论等一般都会有长度限制,短链接更好地帮助我们减少url占用的长度,从而能够填写更多的信息。
- 短链接比较简短,我们有时候可以直接拼出短链接去访问网站(虽然一般也不这么弄)。
总的来说,短链接就是用来解决占用长度问题,一条100字节的url使用tinyUrl可能只要30字节。
短链接设计
短链接一般都是由数字和字母组成,组成比较短的组合。我这边使用了自增ID的方法,对ID进行了Base58编码。Base58编码就是相比Base64编码不使用数字"0",字母大写"O",字母大写"I",和字母小写"l",以及"+“和”/"符号,因为这些字母数字容易让客人看错。
自增ID
自增ID是一种无碰撞的方法,依靠数据库生成唯一ID,然后通过Base58把十进制的ID编码,转成了一个58进制的字符串。
然后解码的时候,把字符串转成十进制即可。
数据库设计
create table tiny_url
(
id bigint not null comment '主键编号'
primary key,
url varchar(1000) null comment 'url',
create_date datetime null comment '创建时间',
expire_date datetime null comment '过期时间',
last_update datetime null comment '最后访问时间',
create_ip varchar(50) null comment '创建IP',
state int(2) null comment '状态(-1-删除 0-禁用 1-正常)'
);
库表设计中,我们可以根据自身的需求来设计,不过最基础的数据也必须存在的。如上面创建表的SQL语句,这是基础的信息。
过期时间设置
我们可以在短链接表中添加过期时间,然后在程序中设计定时任务,定时去扫描表中过期的数据。如果过期了,将数据进行逻辑删除,避免再次使用。
缓存设计
我们可以使用Caffeine本地缓存,设置好缓存过期时间,当超过过期时间后缓存自动移除(这要看具体业务)。当获取短链接的时候,会从缓存中获取,如果获取不到Caffeine从数据库中获取。这是基础处理方式,如果要应对缓存击穿、穿透和雪崩,要自行处理(短链接一般不会有这些问题)。
短链接跳转流程
- 客人访问短链接。
- 从短链接中获取到Base58编码后的ID,然后把ID解码,获取到对应的数据库主键ID。
- 根据此ID从缓存中获取长链接,如果获取到则返回缓存数据;如果获取不到则从数据库中查询,返回长链接。
- 如果长链接不存在,则进行错误统计,返回404;否则返回HTTP状态码302,跳转到长链接。
以上是短链接跳转的基本流程,你也可以添加ip限制等处理,不让客人频繁访问。我们这里使用了HTTP状态码302了,网上也说用301更好,但是业务上不太需要,所以没使用。
为什么要使用302跳转,而不是301跳转呢?
301是永久重定向,302是临时重定向。短地址一经生成就不会变化,所以用301是符合http语义的。但是如果用了301, Google,百度等搜索引擎,搜索的时候会直接展示真实地址,那我们就无法统计到短地址被点击的次数了,也无法收集用户的Cookie, User Agent 等信息,这些信息可以用来做很多有意思的大数据分析,也是短网址服务商的主要盈利来源。原文链接
Base58算法
public class Base58Utils {
private static final char[] DIGITS_58_CODES = {'T', 'o', '9', 'u', 'M', 'W', 's', 'C', '6', 'p', '8', 'g', 'e', 'Z', 'f', 'k', 'N', 'J', 'i', 'v', 'r', '4', 'h', 'b', 'j', 'D', 'z', 'Q', 'G', 'Y', 'A', 't', 'a', 'd', 'R', 'x', 'c', '5', 'q', 'K', 'n', 'H', 'E', 'U', 'y', 'V', 'm', '2', 'w', 'B', 'X', '7', 'S', 'F', 'L', '3', 'P', '1'};
private static int DIGITS_LEN = 58;
/**
* base 58编码。
*
* @param num
* @return
*/
public static String encodeBase58(long num) {
char[] buf = new char[32];
int charPos = 32;
while ((num / DIGITS_LEN) > 0) {
buf[--charPos] = DIGITS_58_CODES[(int) (num % DIGITS_LEN)];
num /= DIGITS_LEN;
}
buf[--charPos] = DIGITS_58_CODES[(int) (num % DIGITS_LEN)];
return new String(buf, charPos, (32 - charPos));
}
/**
* base 58解码。
*
* @param code
* @return
*/
public static long decodeBase58(String code) {
char[] charBuf = code.toCharArray();
long result = 0, base = 1;
for (int i = charBuf.length - 1; i >= 0; i--) {
int index = 0;
for (int j = 0, length = DIGITS_58_CODES.length; j < length; j++) {
// 找到对应字符的下标,对应的下标才是具体的数值
if (DIGITS_58_CODES[j] == charBuf[i]) {
index = j;
}
}
result += index * base;
base *= DIGITS_LEN;
}
return result;
}
/**
* 乱序加密数字。
*
* @param num
* @return
*/
public static long encodeNum(long num) {
if (num < 0) {
num = Math.abs(num);
}
String data = String.valueOf(num);
int mask =(int) (num % 9) + 1;
int pos = mask;
if (mask > data.length()) {
pos = mask % data.length();
} else if (mask == data.length()) {
pos = 1;
}
num = Long.parseLong(mask + data.substring(pos) + data.substring(0, pos));
return num;
}
/**
* 乱序解密数字。
*
* @param num
* @return
*/
public static long decodeNum(long num) {
String data = String.valueOf(num);
int mask = Integer.parseInt(data.substring(0, 1));
int len = data.length() - 1;
int pos = mask;
if (mask > len) {
pos = mask % len;
} else if (mask == len) {
pos = 1;
}
pos = len - pos;
return Long.parseLong(data.substring(pos + 1) + data.substring(1, pos + 1));
}
}
总结
我们在生活中经常使用短链接。发短信,发布评论等一般都会有长度限制,短链接更好地帮助我们减少url占用的长度,从而能够填写更多的信息。
短链接的实现也不复杂,使用了数据库自增长ID,然后通过Base58编码,使得十进制的ID转成了58进制,大大缩短了ID长度。然后通过数据库中实际存放的长链接重定向,体现短链接作用。
评论区