MyChat 群组 ID 的生成
每个群组在创建时由后端分配 ID, 这个 ID 考虑到后面的集群情况, 所以需要做到全局唯一, 所以这里使用了雪花算法来实现
算法实现
package org.lvgo.mychat.server.util;
import java.net.Inet4Address;
import java.net.UnknownHostException;
import java.util.Random;
/**
* MyChatID
* <p>
* MyChat 通用 ID 生成工具
*
* <p>
* 欢迎跟我一起学习,微信(lvgocc)公众号:星尘的一个朋友
*
* @author lvgorice@gmail.com
* @version 1.0
* @blog @see http://lvgo.org
* @CSDN @see https://blog.csdn.net/sinat_34344123
* @date 2021/2/7
*/
public class MyChatID {
/*↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=雪花算法分段各部分长度信息↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=*/
/**
* 时间部分所占长度
*/
private static final int TIMESTAMP_LEN = 41;
/**
* 机器网络标识长度
*/
private static final int MAC_NET_LEN = 5;
private static final int MAC_NET_MAX = ~(-1 << MAC_NET_LEN); // 31
/**
* 机器名称标识长度
*/
private static final int MAC_NAME_LEN = 5;
private static final int MAC_NAME_MAX = ~(-1 << MAC_NAME_LEN); // 31
/**
* 毫秒内序列所占长度
*/
private static final int SEQ_LEN = 12;
private static final int SEQ_MAX = ~(-1 << SEQ_LEN);
/*↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=雪花算法分段各部分长度信息↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=*/
/*↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=分段各部分位置调整↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=*/
private static final byte TIMESTAMP_OFFSET = Long.SIZE - 1 - TIMESTAMP_LEN;
private static final byte MAC_NET_OFFSET = TIMESTAMP_OFFSET - MAC_NET_LEN;
private static final byte MAC_NAME_OFFSET = MAC_NET_OFFSET - MAC_NAME_LEN;
/*↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=分段各部分位置调整↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=*/
/**
* 机器ip标识
*/
private static final long MAC_NET_ID = getMachineNetId();
/**
* 机器名称标识
*/
private static final long MAC_NAME_ID = getMachineNameId();
/**
* 一毫秒内的序列号
*/
private static long sequence = 0;
/**
* 上次生成序列号时间(毫秒)
*/
private static long lastGenTime = -1;
private static long getMachineNetId() {
int macNetValue = 0;
try {
byte[] macNet = Inet4Address.getLocalHost().getAddress();
for (byte b : macNet) {
macNetValue += b;
}
macNetValue = macNetValue & MAC_NET_MAX;
} catch (UnknownHostException e) {
macNetValue = new Random().nextInt(32);
}
return macNetValue;
}
private static long getMachineNameId() {
int macNameValue = 0;
try {
byte[] macName = Inet4Address.getLocalHost().getHostName().getBytes();
for (byte b : macName) {
macNameValue += b;
}
macNameValue = macNameValue & MAC_NAME_MAX;
} catch (UnknownHostException e) {
macNameValue = new Random().nextInt(32);
}
return macNameValue;
}
public synchronized static long nextId() {
long now = System.currentTimeMillis();
// 如果小于上次生成时间, 可以优化为重新生成, 不抛出异常
if (now < lastGenTime) {
throw new IllegalStateException("ERROR TIME");
}
if (now == lastGenTime) {
sequence = ++sequence & SEQ_MAX;
if (sequence == 0) {
now = System.currentTimeMillis();
while (now <= lastGenTime) {
now = System.currentTimeMillis();
}
}
} else {
sequence = 0;
}
lastGenTime = now;
return (now << TIMESTAMP_OFFSET) | (MAC_NET_ID << MAC_NET_OFFSET) | (MAC_NAME_ID << MAC_NAME_OFFSET) | sequence;
}
}
测试
package org.lvgo.mychat.server.util;
import org.junit.jupiter.api.Test;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
/**
* MyChatIDTest
* <p>
* 欢迎跟我一起学习,微信(lvgocc)公众号:星尘的一个朋友
*
* @author lvgorice@gmail.com
* @version 1.0
* @blog @see http://lvgo.org
* @CSDN @see https://blog.csdn.net/sinat_34344123
* @date 2021/2/7
*/
class MyChatIDTest {
@Test
void nextId() throws InterruptedException {
long start = System.currentTimeMillis();
Set<Long> ids = Collections.synchronizedSet(new HashSet<>());
CountDownLatch countDownLatch = new CountDownLatch(10);
for (int i = 0; i < 10; i++) {
new Thread(() -> {
for (int j = 0; j < 10000; j++) {
long genId = MyChatID.nextId();
// System.out.printf("%64s%n",Long.toBinaryString(genId));
System.out.println(genId);
ids.add(genId);
}
countDownLatch.countDown();
}).start();
}
countDownLatch.await();
System.out.printf("生成id %d个, 耗时: %d秒%n", ids.size(), (System.currentTimeMillis() - start) / 1000);
}
}