- 一般比较多的长链接转短链接的方法是对URL计算MD5,然后对MD5取指定位数的随机值。但是这种会导致冲突,可能需要在处理冲突产生比较大的开销。
- 可以使用类似雪花ID的方法来产生随机数,这样可以保证随机数的递增。
- 思路如下:
产生一个54位的long值,组成:机器id(3位) + 时间戳(41位) + 随机值(10位),一共54位
然后没6位将其转化为64进制,每一位分别用:
0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+-
来替代。最后产生一个短链接。
代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.ThreadLocalRandom;
@Slf4j
public class ShortLink {
public ShortLink(byte machineId) {
this.machineId = machineId;
}
/**
* 用于生成短链接的字符
*/
private final static char[] AVAILABLE_CHARS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+-".toCharArray();
/**
* 用于产生随机数
*/
private final ThreadLocalRandom random = ThreadLocalRandom.current();
/**
* 末尾随机值所占位数
*/
private final static int RANDOM_ID_COUNT = 1 << 10;
/**
* 机器id
*/
private final byte machineId;
public String nextShortLink(String url){
return generateShortLink(machineId, url);
}
/**
* 生成短链接
* 组成:机器id(3位) + 时间戳(41位) + 随机值(10位),一共54位
*
* @param machineId 机器id
* @param url url
* @return {@link String}
*/
private String generateShortLink(byte machineId, String url) {
long id = generateId(machineId, url);
char[] chars = new char[9];
chars[0] = AVAILABLE_CHARS[(int) (id & 0x3fL)];
chars[1] = AVAILABLE_CHARS[(int) ((id & 0xfc0L) >> 6)];
chars[2] = AVAILABLE_CHARS[(int) ((id & 0x3f000L) >> 12)];
chars[3] = AVAILABLE_CHARS[(int) ((id & 0xfc0000L) >> 18)];
chars[4] = AVAILABLE_CHARS[(int) ((id & 0x3f000000L) >> 24)];
chars[5] = AVAILABLE_CHARS[(int) ((id & 0xfc0000000L) >> 30)];
chars[6] = AVAILABLE_CHARS[(int) ((id & 0x3f000000000L) >> 36)];
chars[7] = AVAILABLE_CHARS[(int) ((id & 0xfc0000000000L) >> 42)];
chars[8] = AVAILABLE_CHARS[(int) ((id & 0x3f000000000000L) >> 48)];
return new String(chars);
}
/**
* 生成id,用于后续生成短网址
*
* @param machineId 机器id
* @param url url
* @return {@link long}
*/
private long generateId(byte machineId, String url) {
if (machineId < 0 || machineId > 7) {
throw new RuntimeException("机器ID错误");
}
int hashCode = url.hashCode();
long timestamp = System.currentTimeMillis();
long randomId = hash(hashCode, timestamp) % RANDOM_ID_COUNT;
log.info("机器id为:{},对应的二进制为:{};时间戳为:{},对应的二进制为:{};随机值为:{},对应的二进制为:{}"
, machineId, Long.toBinaryString(machineId), timestamp, Long.toBinaryString(timestamp), randomId, Long.toBinaryString(randomId));
return ((long) machineId << 51) | (timestamp << 10) | (randomId);
}
/**
* 计算一个hash,由于hash只有32位,所以要扩散
*
* @param hashcode hashcode
* @param timestamp 时间戳
* @return long
*/
private long hash(long hashcode, long timestamp) {
long randomVal = random.nextLong(timestamp & hashcode);
return randomVal ^ timestamp;
}
}