总体设计思路为每5个bit进行一次编码,编码规则为将数字和大写英文的结合,数字中去掉同意混淆的0和1,英文字母中去掉容易混淆的I和O。这样就不会出现类似于1I0O这种的字符串。
我们一共采用60个bit,每20个bit组成一组,每组之间用-来表示。(例如:T4VS-CSA3-2223)。
详细说明
41bit值即为获取当前时间戳(java 中 System.currentTimeMillis(),js:new Date().getTime()),将其转换为二进制位41位,(例如2024年8月20日17点37分大约为11001000101101111001001011011010010100101)其为精确到毫秒。 考虑到在大型的IM系统中可能存在统一毫秒可能生成多条消息id的情况,故低位采用的为14bit的自旋id,共16383个。
最后5bit为消息类型,包括单聊、群聊等。
具体代码试下逻辑如下:
import java.util.concurrent.atomic.AtomicInteger;
public class FingerprintV2 {
private static final char[] Bits = {
'2', '3', '4', '5', '6', '7', '8','9', 'A', 'B', 'C', 'D', 'E',
'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S',
'T', 'U', 'V', 'W', 'X', 'Y', 'Z'
};
private static final int SPIN_ID_LIMIT = 0x3FFF;
private final static AtomicInteger INCR = new AtomicInteger(0); // 递增值
public static String createFinger(int talkType) {
String s = getHighChar() + getSpinStr() + getTypeStr(talkType);
return encodeBitsToChars(s);
}
//自旋id
private static int createSpinId() {
int ret = INCR.getAndIncrement();
if(INCR.get() >= SPIN_ID_LIMIT) {
INCR.set(0);
}
return ret;
}
/**
* 获取高位字符
* @return
*/
public static String getHighChar() {
long currentTimeMillis = System.currentTimeMillis();
System.out.println(Long.toBinaryString(currentTimeMillis));
return Long.toBinaryString(currentTimeMillis);
}
/**
* 获取自旋字符
* @return
*/
public static String getSpinStr() {
int spinId = createSpinId();
String s = Integer.toBinaryString(spinId);
return String.format("%14s", s.replaceAll(" ", "")).replace(' ', '0');
}
/**
* 获取会话类型
* @param type
* @return
*/
public static String getTypeStr(int type) {
String s = Integer.toBinaryString(type);
return String.format("%5s", s.replaceAll(" ", "")).replace(' ', '0');
}
//将二进制字符编码
public static String encodeBitsToChars(String bitString) {
int length = bitString.length();
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < bitString.length(); i+=5) {
int end = Math.min(i + 5, length);
String substring = bitString.substring(i, end);
int decimalValue = Integer.parseInt(substring, 2);
char bit = Bits[decimalValue];
stringBuilder.append(bit);
}
String string = stringBuilder.toString();
return splitAndJoin(string, 4, '-');
}
//字符串拼接
public static String splitAndJoin(String input, int chunkSize, char separator) {
StringBuilder output = new StringBuilder();
int length = input.length();
if (length == 0) {
return "";
}
for (int i = 0; i < length; i += chunkSize) {
int end = Math.min(i + chunkSize, length);
String substring = input.substring(i, end);
if (output.length() > 0) {
output.append(separator);
}
output.append(substring);
}
return output.toString();
}
循环执行1万此:
public static void main(String[] args) {
Set<String> set = new HashSet<>();
for (int i = 0; i < 10000; i++) {
String finger = createFinger(1);
System.out.println(finger);
set.add(finger);
}
System.out.println(set.size());
}